@nickyzj2023/utils 1.0.2 → 1.0.4

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/src/network.ts CHANGED
@@ -1,90 +1,100 @@
1
- import { isObject } from "./is";
2
- import LRUCache from "./lru-cache";
3
-
4
- const cachedRequests = new LRUCache<string, Promise<Response>>();
5
-
6
- /**
7
- * 基于 Fetch API 的请求客户端
8
- * @example
9
- * // 用法1:创建客户端
10
- * const api = fetcher("https://nickyzj.run:3030");
11
- * const res = await api.get<Blog>("/blogs/2025/猩猩也能懂的Node.js部署教程");
12
- *
13
- * // 用法2:直接发送请求
14
- * const res = await fetcher().get<Blog>("https://nickyzj.run:3030/blogs/2025/猩猩也能懂的Node.js部署教程");
15
- */
16
- export const fetcher = (baseURL = "") => {
17
- const createRequest = async <T>(path: string, options: RequestInit = {}) => {
18
- // 构建完整 URL
19
- const url = baseURL ? `${baseURL}${path}` : path;
20
-
21
- // 处理 body 为对象的情况
22
- if (isObject(options.body)) {
23
- options.body = JSON.stringify(options.body);
24
- options.headers = {
25
- ...options.headers,
26
- "Content-Type": "application/json",
27
- };
28
- }
29
-
30
- const request = () => fetch(url, options);
31
- let promise: Promise<Response>;
32
-
33
- const canCache = options.method === "GET" || !options.method;
34
- if (!canCache) {
35
- promise = request();
36
- } else {
37
- let tempPromise = cachedRequests.get(url);
38
- if (!tempPromise) {
39
- tempPromise = request();
40
- cachedRequests.set(url, tempPromise);
41
- }
42
- promise = tempPromise;
43
- }
44
-
45
- // 必须使用 clone() 消费一个新的响应体,否则下次从 cache 中获取的响应体会报错(无法被重复消费)
46
- const response = (await promise).clone();
47
- if (!response.ok) {
48
- throw new Error(response.statusText);
49
- }
50
-
51
- const data = await response.json();
52
- return data as T;
53
- };
54
-
55
- return {
56
- get: <T>(url: string, options: Omit<RequestInit, "method"> = {}) =>
57
- createRequest<T>(url, { ...options, method: "GET" }),
58
-
59
- post: <T>(
60
- url: string,
61
- body?: any,
62
- options: Omit<RequestInit, "method" | "body"> = {},
63
- ) => createRequest<T>(url, { ...options, method: "POST", body }),
64
-
65
- put: <T>(
66
- url: string,
67
- body?: any,
68
- options: Omit<RequestInit, "method" | "body"> = {},
69
- ) => createRequest<T>(url, { ...options, method: "PUT", body }),
70
-
71
- delete: <T>(url: string, options: Omit<RequestInit, "method"> = {}) =>
72
- createRequest<T>(url, { ...options, method: "DELETE" }),
73
- };
74
- };
75
-
76
- /**
77
- * Go 语言风格的异步处理方式
78
- * @example
79
- * const [error, response] = await to(request<Resp>("/blogs/2025/猩猩也能懂的Node.js部署教程"));
80
- */
81
- export const to = async <T, U = Error>(
82
- promise: Promise<T>,
83
- ): Promise<[null, T] | [U, undefined]> => {
84
- try {
85
- const response = await promise;
86
- return [null, response];
87
- } catch (error) {
88
- return [error as U, undefined];
89
- }
90
- };
1
+ import { isObject } from "./is";
2
+ import LRUCache from "./lru-cache";
3
+
4
+ const cachedRequests = new LRUCache<string, Promise<Response>>();
5
+
6
+ /**
7
+ * 基于 Fetch API 的请求客户端
8
+ * @param baseURL 接口前缀,如 https://nickyzj.run:3030,也可以不填
9
+ *
10
+ * @remarks
11
+ * 特性:
12
+ * - 在 body 里直接传递对象
13
+ * - 能够缓存 GET 请求
14
+ *
15
+ * @example
16
+ * // 用法1:创建客户端
17
+ * const api = fetcher("https://nickyzj.run:3030");
18
+ * const res = await api.get<Blog>("/blogs/hello-world");
19
+ *
20
+ * // 用法2:直接发送请求
21
+ * const res = await fetcher().get<Blog>("https://nickyzj.run:3030/blogs/hello-world");
22
+ */
23
+ export const fetcher = (baseURL = "") => {
24
+ const createRequest = async <T>(path: string, options: RequestInit = {}) => {
25
+ // 构建完整 URL
26
+ const url = baseURL ? `${baseURL}${path}` : path;
27
+
28
+ // 处理 body 为对象的情况
29
+ if (isObject(options.body)) {
30
+ options.body = JSON.stringify(options.body);
31
+ options.headers = {
32
+ ...options.headers,
33
+ "Content-Type": "application/json",
34
+ };
35
+ }
36
+
37
+ const request = () => fetch(url, options);
38
+ let promise: Promise<Response>;
39
+
40
+ const canCache = options.method === "GET" || !options.method;
41
+ if (!canCache) {
42
+ promise = request();
43
+ } else {
44
+ let tempPromise = cachedRequests.get(url);
45
+ if (!tempPromise) {
46
+ tempPromise = request();
47
+ cachedRequests.set(url, tempPromise);
48
+ }
49
+ promise = tempPromise;
50
+ }
51
+
52
+ // 必须使用 clone() 消费一个新的响应体,否则下次从 cache 中获取的响应体会报错(无法被重复消费)
53
+ const response = (await promise).clone();
54
+ if (!response.ok) {
55
+ throw new Error(response.statusText);
56
+ }
57
+
58
+ const data = await response.json();
59
+ return data as T;
60
+ };
61
+
62
+ return {
63
+ get: <T>(url: string, options: Omit<RequestInit, "method"> = {}) =>
64
+ createRequest<T>(url, { ...options, method: "GET" }),
65
+
66
+ post: <T>(
67
+ url: string,
68
+ body?: any,
69
+ options: Omit<RequestInit, "method" | "body"> = {},
70
+ ) => createRequest<T>(url, { ...options, method: "POST", body }),
71
+
72
+ put: <T>(
73
+ url: string,
74
+ body?: any,
75
+ options: Omit<RequestInit, "method" | "body"> = {},
76
+ ) => createRequest<T>(url, { ...options, method: "PUT", body }),
77
+
78
+ delete: <T>(url: string, options: Omit<RequestInit, "method"> = {}) =>
79
+ createRequest<T>(url, { ...options, method: "DELETE" }),
80
+ };
81
+ };
82
+
83
+ /**
84
+ * Go 语言风格的异步处理方式
85
+ * @param promise 一个能被 await 的异步函数
86
+ * @returns 如果成功,返回 [null, 异步函数结果],否则返回 [Error, undefined]
87
+ *
88
+ * @example
89
+ * const [error, response] = await to(fetcher().get<Blog>("/blogs/hello-world"));
90
+ */
91
+ export const to = async <T, Error>(
92
+ promise: Promise<T>,
93
+ ): Promise<[null, T] | [Error, undefined]> => {
94
+ try {
95
+ const response = await promise;
96
+ return [null, response];
97
+ } catch (error) {
98
+ return [error as Error, undefined];
99
+ }
100
+ };
package/tsconfig.json CHANGED
@@ -1,32 +1,32 @@
1
- {
2
- "compilerOptions": {
3
- // Environment setup & latest features
4
- "lib": ["ESNext", "DOM"],
5
- "target": "ESNext",
6
- "module": "Preserve",
7
- "moduleDetection": "force",
8
- "jsx": "react-jsx",
9
- "allowJs": true,
10
-
11
- // Bundler mode
12
- "moduleResolution": "bundler",
13
- "allowImportingTsExtensions": true,
14
- "verbatimModuleSyntax": true,
15
- "outDir": "dist",
16
- "declaration": true,
17
- "emitDeclarationOnly": true,
18
-
19
- // Best practices
20
- "strict": true,
21
- "skipLibCheck": true,
22
- "noFallthroughCasesInSwitch": true,
23
- "noUncheckedIndexedAccess": true,
24
- "noImplicitOverride": true,
25
-
26
- // Some stricter flags (disabled by default)
27
- "noUnusedLocals": false,
28
- "noUnusedParameters": false,
29
- "noPropertyAccessFromIndexSignature": false
30
- },
31
- "include": ["src"]
32
- }
1
+ {
2
+ "compilerOptions": {
3
+ // Environment setup & latest features
4
+ "lib": ["ESNext", "DOM"],
5
+ "target": "ESNext",
6
+ "module": "Preserve",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "allowJs": true,
10
+
11
+ // Bundler mode
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "verbatimModuleSyntax": true,
15
+ "outDir": "dist",
16
+ "declaration": true,
17
+ "emitDeclarationOnly": true,
18
+
19
+ // Best practices
20
+ "strict": true,
21
+ "skipLibCheck": true,
22
+ "noFallthroughCasesInSwitch": true,
23
+ "noUncheckedIndexedAccess": true,
24
+ "noImplicitOverride": true,
25
+
26
+ // Some stricter flags (disabled by default)
27
+ "noUnusedLocals": false,
28
+ "noUnusedParameters": false,
29
+ "noPropertyAccessFromIndexSignature": false
30
+ },
31
+ "include": ["src"]
32
+ }