@kawaiininja/fetch 1.0.1
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/context/ApiContext.d.ts +41 -0
- package/dist/context/ApiContext.js +37 -0
- package/dist/context/index.d.ts +1 -0
- package/dist/context/index.js +1 -0
- package/dist/hooks/index.d.ts +5 -0
- package/dist/hooks/index.js +5 -0
- package/dist/hooks/types.d.ts +24 -0
- package/dist/hooks/types.js +1 -0
- package/dist/hooks/useApiConfig.d.ts +8 -0
- package/dist/hooks/useApiConfig.js +15 -0
- package/dist/hooks/useCsrf.d.ts +4 -0
- package/dist/hooks/useCsrf.js +43 -0
- package/dist/hooks/useFetch.d.ts +9 -0
- package/dist/hooks/useFetch.js +128 -0
- package/dist/hooks/utils.d.ts +38 -0
- package/dist/hooks/utils.js +104 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +40 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Internal service header for API requests
|
|
4
|
+
*/
|
|
5
|
+
export declare const INTERNAL_HEADER: {
|
|
6
|
+
readonly "x-internal-service": "zevrinix-main";
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* API configuration interface
|
|
10
|
+
*/
|
|
11
|
+
export interface ApiConfig {
|
|
12
|
+
baseUrl: string;
|
|
13
|
+
version?: string;
|
|
14
|
+
disableCsrf?: boolean;
|
|
15
|
+
onError?: (error: string, status: number | null) => void;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* API context value type
|
|
19
|
+
*/
|
|
20
|
+
export interface ApiContextValue {
|
|
21
|
+
baseUrl: string;
|
|
22
|
+
version: string;
|
|
23
|
+
disableCsrf: boolean;
|
|
24
|
+
apiUrl: (path: string) => string;
|
|
25
|
+
onError?: (error: string, status: number | null) => void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Props for ApiProvider component
|
|
29
|
+
*/
|
|
30
|
+
export interface ApiProviderProps {
|
|
31
|
+
config: ApiConfig;
|
|
32
|
+
children: React.ReactNode;
|
|
33
|
+
}
|
|
34
|
+
export declare const ApiContext: React.Context<ApiContextValue | null>;
|
|
35
|
+
/**
|
|
36
|
+
* ApiProvider
|
|
37
|
+
*
|
|
38
|
+
* Provides API configuration to the component tree.
|
|
39
|
+
* Constructs full API URLs from relative paths using the baseUrl.
|
|
40
|
+
*/
|
|
41
|
+
export declare function ApiProvider({ config, children }: ApiProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useMemo } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Internal service header for API requests
|
|
5
|
+
*/
|
|
6
|
+
export const INTERNAL_HEADER = {
|
|
7
|
+
"x-internal-service": "zevrinix-main",
|
|
8
|
+
};
|
|
9
|
+
export const ApiContext = createContext(null);
|
|
10
|
+
/**
|
|
11
|
+
* ApiProvider
|
|
12
|
+
*
|
|
13
|
+
* Provides API configuration to the component tree.
|
|
14
|
+
* Constructs full API URLs from relative paths using the baseUrl.
|
|
15
|
+
*/
|
|
16
|
+
export function ApiProvider({ config, children }) {
|
|
17
|
+
const value = useMemo(() => {
|
|
18
|
+
// Ensure valid config structure
|
|
19
|
+
const baseUrl = config?.baseUrl || "";
|
|
20
|
+
const version = config?.version || "v1";
|
|
21
|
+
return {
|
|
22
|
+
baseUrl,
|
|
23
|
+
version,
|
|
24
|
+
disableCsrf: !!config?.disableCsrf,
|
|
25
|
+
onError: config?.onError,
|
|
26
|
+
// Helper to construct URLs
|
|
27
|
+
apiUrl: (path) => {
|
|
28
|
+
const cleanBase = baseUrl.endsWith("/")
|
|
29
|
+
? baseUrl.slice(0, -1)
|
|
30
|
+
: baseUrl;
|
|
31
|
+
const cleanPath = path.startsWith("/") ? path : `/${path}`;
|
|
32
|
+
return `${cleanBase}${cleanPath}`;
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}, [config]);
|
|
36
|
+
return _jsx(ApiContext.Provider, { value: value, children: children });
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./ApiContext";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./ApiContext";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch state interface
|
|
3
|
+
*/
|
|
4
|
+
export interface FetchState<T = any> {
|
|
5
|
+
data: T | null;
|
|
6
|
+
loading: boolean;
|
|
7
|
+
error: string | null;
|
|
8
|
+
status: number | null;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Base fetch options
|
|
12
|
+
*/
|
|
13
|
+
export interface BaseFetchOptions extends RequestInit {
|
|
14
|
+
parseAs?: "auto" | "json" | "text" | "blob";
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Request options for the request function
|
|
18
|
+
*/
|
|
19
|
+
export interface RequestOptions extends BaseFetchOptions {
|
|
20
|
+
url?: string;
|
|
21
|
+
method?: string;
|
|
22
|
+
body?: BodyInit;
|
|
23
|
+
headers?: HeadersInit;
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ApiContextValue } from "../context/ApiContext";
|
|
2
|
+
/**
|
|
3
|
+
* Hook to consume API configuration
|
|
4
|
+
*
|
|
5
|
+
* @returns API context value with baseUrl, version, and apiUrl helper
|
|
6
|
+
* @throws Error if used outside ApiProvider
|
|
7
|
+
*/
|
|
8
|
+
export declare function useApiConfig(): ApiContextValue;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { ApiContext } from "../context/ApiContext";
|
|
3
|
+
/**
|
|
4
|
+
* Hook to consume API configuration
|
|
5
|
+
*
|
|
6
|
+
* @returns API context value with baseUrl, version, and apiUrl helper
|
|
7
|
+
* @throws Error if used outside ApiProvider
|
|
8
|
+
*/
|
|
9
|
+
export function useApiConfig() {
|
|
10
|
+
const context = useContext(ApiContext);
|
|
11
|
+
if (!context) {
|
|
12
|
+
throw new Error("useApiConfig must be used within an ApiProvider");
|
|
13
|
+
}
|
|
14
|
+
return context;
|
|
15
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useCallback, useRef } from "react";
|
|
2
|
+
import { INTERNAL_HEADER } from "../context/ApiContext";
|
|
3
|
+
import { useApiConfig } from "./useApiConfig";
|
|
4
|
+
export function useCsrf() {
|
|
5
|
+
const { apiUrl } = useApiConfig();
|
|
6
|
+
const csrfRef = useRef(null);
|
|
7
|
+
const clearCsrf = useCallback(() => {
|
|
8
|
+
csrfRef.current = null;
|
|
9
|
+
if (typeof window !== "undefined") {
|
|
10
|
+
localStorage.removeItem("csrf_token");
|
|
11
|
+
}
|
|
12
|
+
}, []);
|
|
13
|
+
const fetchCSRF = useCallback(async () => {
|
|
14
|
+
if (csrfRef.current)
|
|
15
|
+
return csrfRef.current;
|
|
16
|
+
// Try to get from localStorage first (for Capacitor/mobile)
|
|
17
|
+
if (typeof window !== "undefined") {
|
|
18
|
+
const storedCsrf = localStorage.getItem("csrf_token");
|
|
19
|
+
if (storedCsrf) {
|
|
20
|
+
csrfRef.current = storedCsrf;
|
|
21
|
+
return storedCsrf;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Construct CSRF URL using the context's baseUrl
|
|
25
|
+
const csrfUrl = apiUrl("auth/csrf-token");
|
|
26
|
+
const res = await fetch(csrfUrl, {
|
|
27
|
+
method: "GET",
|
|
28
|
+
headers: INTERNAL_HEADER,
|
|
29
|
+
credentials: "include",
|
|
30
|
+
});
|
|
31
|
+
const json = await res.json();
|
|
32
|
+
const token = json?.csrfToken;
|
|
33
|
+
if (!token)
|
|
34
|
+
throw new Error("Missing CSRF token");
|
|
35
|
+
csrfRef.current = token;
|
|
36
|
+
// Store in localStorage for Capacitor/mobile (cookies don't work cross-domain)
|
|
37
|
+
if (typeof window !== "undefined") {
|
|
38
|
+
localStorage.setItem("csrf_token", token);
|
|
39
|
+
}
|
|
40
|
+
return token;
|
|
41
|
+
}, [apiUrl]);
|
|
42
|
+
return { fetchCSRF, clearCsrf };
|
|
43
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { BaseFetchOptions } from "./types";
|
|
2
|
+
import { ApiSurface } from "./utils";
|
|
3
|
+
/**
|
|
4
|
+
* useFetch Hook
|
|
5
|
+
*
|
|
6
|
+
* A comprehensive hook for data fetching with automatic CSRF handling,
|
|
7
|
+
* request cancellation, and a rich API surface.
|
|
8
|
+
*/
|
|
9
|
+
export declare const useFetch: <T = any>(endpoint: string, baseOptions?: BaseFetchOptions) => ApiSurface<T>;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { INTERNAL_HEADER } from "../context/ApiContext";
|
|
3
|
+
import { useApiConfig } from "./useApiConfig";
|
|
4
|
+
import { useCsrf } from "./useCsrf";
|
|
5
|
+
import { createApiSurface } from "./utils";
|
|
6
|
+
/**
|
|
7
|
+
* useFetch Hook
|
|
8
|
+
*
|
|
9
|
+
* A comprehensive hook for data fetching with automatic CSRF handling,
|
|
10
|
+
* request cancellation, and a rich API surface.
|
|
11
|
+
*/
|
|
12
|
+
export const useFetch = (endpoint, baseOptions = {}) => {
|
|
13
|
+
const { apiUrl, disableCsrf, onError } = useApiConfig();
|
|
14
|
+
const { fetchCSRF, clearCsrf } = useCsrf();
|
|
15
|
+
const resolvedUrl = endpoint.startsWith("http") ? endpoint : apiUrl(endpoint);
|
|
16
|
+
const abortRef = useRef(new AbortController());
|
|
17
|
+
const mounted = useRef(true);
|
|
18
|
+
const optionsRef = useRef(baseOptions);
|
|
19
|
+
const [state, setState] = useState({
|
|
20
|
+
data: null,
|
|
21
|
+
loading: false,
|
|
22
|
+
error: null,
|
|
23
|
+
status: null,
|
|
24
|
+
});
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
optionsRef.current = baseOptions;
|
|
27
|
+
}, [baseOptions]);
|
|
28
|
+
const safeSet = useCallback((fn) => {
|
|
29
|
+
if (mounted.current) {
|
|
30
|
+
setState((prev) => ({ ...prev, ...fn(prev) }));
|
|
31
|
+
}
|
|
32
|
+
}, []);
|
|
33
|
+
const performFetch = async (url, method, token, body, headers, rest) => {
|
|
34
|
+
const authToken = typeof window !== "undefined" ? localStorage.getItem("token") : null;
|
|
35
|
+
const sessionId = typeof window !== "undefined" ? localStorage.getItem("session_id") : null;
|
|
36
|
+
const headersConfig = {
|
|
37
|
+
...(optionsRef.current.headers || {}),
|
|
38
|
+
...(headers || {}),
|
|
39
|
+
...INTERNAL_HEADER,
|
|
40
|
+
"X-CSRF-Token": token,
|
|
41
|
+
...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),
|
|
42
|
+
...(sessionId ? { "X-Session-ID": sessionId } : {}),
|
|
43
|
+
};
|
|
44
|
+
return fetch(url, {
|
|
45
|
+
...optionsRef.current,
|
|
46
|
+
...rest,
|
|
47
|
+
method,
|
|
48
|
+
signal: abortRef.current.signal,
|
|
49
|
+
credentials: "include",
|
|
50
|
+
headers: headersConfig,
|
|
51
|
+
body,
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
const parseResponse = async (res, parseAs) => {
|
|
55
|
+
const type = res.headers.get("content-type") || "";
|
|
56
|
+
if (parseAs === "json" ||
|
|
57
|
+
(parseAs === "auto" && type.includes("application/json"))) {
|
|
58
|
+
return res.json();
|
|
59
|
+
}
|
|
60
|
+
else if (parseAs === "text") {
|
|
61
|
+
return res.text();
|
|
62
|
+
}
|
|
63
|
+
else if (parseAs === "blob") {
|
|
64
|
+
return res.blob();
|
|
65
|
+
}
|
|
66
|
+
return res.text();
|
|
67
|
+
};
|
|
68
|
+
const request = useCallback(async (params = {}) => {
|
|
69
|
+
const { url = resolvedUrl, method = "GET", body, headers, parseAs = "auto", ...rest } = params;
|
|
70
|
+
let finalUrl = url.startsWith("http") ? url : apiUrl(url);
|
|
71
|
+
if (!finalUrl)
|
|
72
|
+
return;
|
|
73
|
+
safeSet(() => ({ loading: true, error: null }));
|
|
74
|
+
let lastStatus = null;
|
|
75
|
+
try {
|
|
76
|
+
let csrfToken = "";
|
|
77
|
+
if (!disableCsrf) {
|
|
78
|
+
csrfToken = await fetchCSRF();
|
|
79
|
+
}
|
|
80
|
+
let res = await performFetch(finalUrl, method, csrfToken, body, headers, rest);
|
|
81
|
+
lastStatus = res.status;
|
|
82
|
+
// 🔄 Auto-retry on CSRF error
|
|
83
|
+
if (!disableCsrf && res.status === 403) {
|
|
84
|
+
try {
|
|
85
|
+
const errorBody = await res.clone().json();
|
|
86
|
+
if (errorBody?.code === "CSRF_ERROR" ||
|
|
87
|
+
errorBody?.message === "Invalid or missing CSRF token") {
|
|
88
|
+
clearCsrf();
|
|
89
|
+
csrfToken = await fetchCSRF();
|
|
90
|
+
res = await performFetch(finalUrl, method, csrfToken, body, headers, rest);
|
|
91
|
+
lastStatus = res.status;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
/* ignore parse error */
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const parsed = await parseResponse(res, parseAs);
|
|
99
|
+
if (!res.ok) {
|
|
100
|
+
throw new Error(parsed?.message || res.statusText);
|
|
101
|
+
}
|
|
102
|
+
safeSet(() => ({ data: parsed, status: res.status }));
|
|
103
|
+
return parsed;
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
if (err.name !== "AbortError") {
|
|
107
|
+
safeSet(() => ({ error: err.message, status: lastStatus }));
|
|
108
|
+
// 📣 Global Error Broadcast
|
|
109
|
+
if (onError) {
|
|
110
|
+
onError(err.message, lastStatus);
|
|
111
|
+
}
|
|
112
|
+
throw err;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
safeSet(() => ({ loading: false }));
|
|
117
|
+
}
|
|
118
|
+
}, [resolvedUrl, fetchCSRF, clearCsrf, apiUrl, safeSet]);
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
abortRef.current = new AbortController();
|
|
121
|
+
mounted.current = true;
|
|
122
|
+
return () => {
|
|
123
|
+
mounted.current = false;
|
|
124
|
+
abortRef.current?.abort();
|
|
125
|
+
};
|
|
126
|
+
}, []);
|
|
127
|
+
return useMemo(() => createApiSurface({ state, request, baseUrl: resolvedUrl, safeSet }), [state, request, resolvedUrl, safeSet]);
|
|
128
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { FetchState, RequestOptions } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* API surface interface with all request methods
|
|
4
|
+
*/
|
|
5
|
+
export interface ApiSurface<T = any> {
|
|
6
|
+
state: FetchState<T>;
|
|
7
|
+
isLoading: boolean;
|
|
8
|
+
isError: boolean;
|
|
9
|
+
isSuccess: boolean;
|
|
10
|
+
request: <R = T>(options?: RequestOptions) => Promise<R | undefined>;
|
|
11
|
+
refetch: () => Promise<T | undefined>;
|
|
12
|
+
setData: (value: T | null | ((prev: T | null) => T | null)) => void;
|
|
13
|
+
updateData: (partial: Partial<T> | ((prev: T | null) => Partial<T>)) => void;
|
|
14
|
+
get: <R = T>(config?: RequestOptions) => Promise<R | undefined>;
|
|
15
|
+
post: <D = any, R = T>(data?: D, config?: RequestOptions) => Promise<R | undefined>;
|
|
16
|
+
put: <D = any, R = T>(data?: D, config?: RequestOptions) => Promise<R | undefined>;
|
|
17
|
+
patch: <D = any, R = T>(data?: D, config?: RequestOptions) => Promise<R | undefined>;
|
|
18
|
+
delete: <R = T>(config?: RequestOptions) => Promise<R | undefined>;
|
|
19
|
+
json: <D = any, R = T>(data?: D, config?: RequestOptions) => Promise<R | undefined>;
|
|
20
|
+
text: (config?: RequestOptions) => Promise<string | undefined>;
|
|
21
|
+
blob: (config?: RequestOptions) => Promise<Blob | undefined>;
|
|
22
|
+
upload: <R = T>(formData: FormData, config?: RequestOptions) => Promise<R | undefined>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Parameters for creating API surface
|
|
26
|
+
*/
|
|
27
|
+
interface CreateApiSurfaceParams<T> {
|
|
28
|
+
state: FetchState<T>;
|
|
29
|
+
request: <R = T>(options?: RequestOptions) => Promise<R | undefined>;
|
|
30
|
+
baseUrl: string;
|
|
31
|
+
safeSet: (fn: (prev: FetchState<T>) => Partial<FetchState<T>>) => void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Helper to construct the API surface object.
|
|
35
|
+
* Extracts the massive object creation from the main hook to keep it clean.
|
|
36
|
+
*/
|
|
37
|
+
export declare const createApiSurface: <T = any>({ state, request, baseUrl, safeSet, }: CreateApiSurfaceParams<T>) => ApiSurface<T>;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper to construct the API surface object.
|
|
3
|
+
* Extracts the massive object creation from the main hook to keep it clean.
|
|
4
|
+
*/
|
|
5
|
+
export const createApiSurface = ({ state, request, baseUrl, safeSet, }) => {
|
|
6
|
+
// little helper for JSON methods
|
|
7
|
+
const withJsonBody = (data) => ({
|
|
8
|
+
body: data != null ? JSON.stringify(data) : undefined,
|
|
9
|
+
headers: { "Content-Type": "application/json" },
|
|
10
|
+
parseAs: "json",
|
|
11
|
+
});
|
|
12
|
+
return {
|
|
13
|
+
// 🔍 state
|
|
14
|
+
state,
|
|
15
|
+
isLoading: state.loading,
|
|
16
|
+
isError: !!state.error,
|
|
17
|
+
isSuccess: !!state.data && !state.error,
|
|
18
|
+
// 🔁 core
|
|
19
|
+
request,
|
|
20
|
+
refetch: () => request({ url: baseUrl }),
|
|
21
|
+
// 🎯 data helpers
|
|
22
|
+
setData: (value) => {
|
|
23
|
+
safeSet((prev) => ({
|
|
24
|
+
data: (typeof value === "function"
|
|
25
|
+
? value(prev.data)
|
|
26
|
+
: value),
|
|
27
|
+
}));
|
|
28
|
+
},
|
|
29
|
+
updateData: (partial) => safeSet((prev) => ({
|
|
30
|
+
data: {
|
|
31
|
+
...prev.data,
|
|
32
|
+
...(typeof partial === "function" ? partial(prev.data) : partial),
|
|
33
|
+
},
|
|
34
|
+
})),
|
|
35
|
+
// 🗡 CRUD methods
|
|
36
|
+
get: (config = {}) => request({ ...config, url: config.url || baseUrl, method: "GET" }),
|
|
37
|
+
post: (data, config = {}) => request({
|
|
38
|
+
...config,
|
|
39
|
+
url: config.url || baseUrl,
|
|
40
|
+
method: "POST",
|
|
41
|
+
...withJsonBody(data),
|
|
42
|
+
headers: {
|
|
43
|
+
...(config.headers || {}),
|
|
44
|
+
"Content-Type": "application/json",
|
|
45
|
+
},
|
|
46
|
+
}),
|
|
47
|
+
put: (data, config = {}) => request({
|
|
48
|
+
...config,
|
|
49
|
+
url: config.url || baseUrl,
|
|
50
|
+
method: "PUT",
|
|
51
|
+
...withJsonBody(data),
|
|
52
|
+
headers: {
|
|
53
|
+
...(config.headers || {}),
|
|
54
|
+
"Content-Type": "application/json",
|
|
55
|
+
},
|
|
56
|
+
}),
|
|
57
|
+
patch: (data, config = {}) => request({
|
|
58
|
+
...config,
|
|
59
|
+
url: config.url || baseUrl,
|
|
60
|
+
method: "PATCH",
|
|
61
|
+
...withJsonBody(data),
|
|
62
|
+
headers: {
|
|
63
|
+
...(config.headers || {}),
|
|
64
|
+
"Content-Type": "application/json",
|
|
65
|
+
},
|
|
66
|
+
}),
|
|
67
|
+
delete: (config = {}) => request({
|
|
68
|
+
...config,
|
|
69
|
+
url: config.url || baseUrl,
|
|
70
|
+
method: "DELETE",
|
|
71
|
+
parseAs: config.parseAs || "json",
|
|
72
|
+
}),
|
|
73
|
+
// 🎭 type-focused helpers
|
|
74
|
+
json: (data, config = {}) => request({
|
|
75
|
+
...config,
|
|
76
|
+
url: config.url || baseUrl,
|
|
77
|
+
method: config.method || "POST",
|
|
78
|
+
...withJsonBody(data),
|
|
79
|
+
headers: {
|
|
80
|
+
...(config.headers || {}),
|
|
81
|
+
"Content-Type": "application/json",
|
|
82
|
+
},
|
|
83
|
+
}),
|
|
84
|
+
text: (config = {}) => request({
|
|
85
|
+
...config,
|
|
86
|
+
url: config.url || baseUrl,
|
|
87
|
+
method: config.method || "GET",
|
|
88
|
+
parseAs: "text",
|
|
89
|
+
}),
|
|
90
|
+
blob: (config = {}) => request({
|
|
91
|
+
...config,
|
|
92
|
+
url: config.url || baseUrl,
|
|
93
|
+
method: config.method || "GET",
|
|
94
|
+
parseAs: "blob",
|
|
95
|
+
}),
|
|
96
|
+
upload: (formData, config = {}) => request({
|
|
97
|
+
...config,
|
|
98
|
+
url: config.url || baseUrl,
|
|
99
|
+
method: config.method || "POST",
|
|
100
|
+
body: formData,
|
|
101
|
+
parseAs: config.parseAs || "json",
|
|
102
|
+
}),
|
|
103
|
+
};
|
|
104
|
+
};
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kawaiininja/fetch",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Core fetch utility for Onyx Framework",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"react",
|
|
21
|
+
"fetch",
|
|
22
|
+
"onyx",
|
|
23
|
+
"kawaiininja",
|
|
24
|
+
"http"
|
|
25
|
+
],
|
|
26
|
+
"author": "Vinay (4kawaiininja)",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
30
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/react": "^19.2.7",
|
|
37
|
+
"@types/react-dom": "^19.2.3",
|
|
38
|
+
"typescript": "^5.7.0"
|
|
39
|
+
}
|
|
40
|
+
}
|