@dune2/tools 1.0.3 → 1.0.5
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/package.json +2 -4
- package/readme.md +105 -2
- package/src/rq/RequestBuilder.react-server.ts +1 -2
- package/src/rq/options.ts +28 -4
- package/src/logger/Logger.ts +0 -104
- package/src/logger/index.ts +0 -71
- package/src/logger/shared.ts +0 -25
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dune2/tools",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"i18n"
|
|
@@ -25,8 +25,6 @@
|
|
|
25
25
|
"./shared/*": "./src/shared/*.ts",
|
|
26
26
|
"./storage": "./src/storage/index.ts",
|
|
27
27
|
"./storage/*": "./src/storage/*.ts",
|
|
28
|
-
"./logger": "./src/logger/index.ts",
|
|
29
|
-
"./logger/*": "./src/logger/*.ts",
|
|
30
28
|
"./store": "./src/store/index.ts",
|
|
31
29
|
"./package.json": "./package.json",
|
|
32
30
|
".": "./src/index.ts"
|
|
@@ -36,7 +34,7 @@
|
|
|
36
34
|
],
|
|
37
35
|
"dependencies": {
|
|
38
36
|
"bignumber.js": "^9.1.2",
|
|
39
|
-
"es-toolkit": "^1
|
|
37
|
+
"es-toolkit": "^1",
|
|
40
38
|
"js-cookie": "^3.0.5",
|
|
41
39
|
"store2": "^2.14.3"
|
|
42
40
|
},
|
package/readme.md
CHANGED
|
@@ -1,3 +1,106 @@
|
|
|
1
|
-
|
|
1
|
+
# @dune2/tools
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
React utility library with common tools and components.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @dune2/tools
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Modules
|
|
12
|
+
|
|
13
|
+
### Factory
|
|
14
|
+
|
|
15
|
+
React state management utilities for creating context providers.
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { createStateContext } from "@dune2/tools/factory/createStateContext";
|
|
19
|
+
|
|
20
|
+
const { Provider, useContextValue } = createStateContext({
|
|
21
|
+
name: "Counter",
|
|
22
|
+
useValueHooks: ({ initialCount = 0 }) => {
|
|
23
|
+
const [count, setCount] = useState(initialCount);
|
|
24
|
+
return { count, setCount };
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Storage
|
|
30
|
+
|
|
31
|
+
Browser storage wrapper with React hooks integration.
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { createStorage } from "@dune2/tools/storage";
|
|
35
|
+
|
|
36
|
+
class DataMap {
|
|
37
|
+
token = "";
|
|
38
|
+
user = null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const storage = createStorage({
|
|
42
|
+
DataMap,
|
|
43
|
+
namespace: "app",
|
|
44
|
+
storageType: "local", // or 'session'
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Use in React
|
|
48
|
+
const token = storage.token.useValue();
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### RQ (React Query)
|
|
52
|
+
|
|
53
|
+
Enhanced React Query utilities with built-in request building.
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { RequestBuilder } from "@dune2/tools/rq";
|
|
57
|
+
|
|
58
|
+
const userApi = new RequestBuilder({
|
|
59
|
+
url: "/api/users/{id}",
|
|
60
|
+
method: "get",
|
|
61
|
+
urlPathParams: ["id"],
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Use in components
|
|
65
|
+
const { data, isLoading } = userApi.useQuery({ id: "123" });
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Store
|
|
69
|
+
|
|
70
|
+
Valtio-based state management with TypeScript support.
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { createStore } from "@dune2/tools/store";
|
|
74
|
+
|
|
75
|
+
const counterStore = createStore({
|
|
76
|
+
name: "counter",
|
|
77
|
+
state: { count: 0 },
|
|
78
|
+
actionsCreator: (state) => ({
|
|
79
|
+
increment: () => state.count++,
|
|
80
|
+
decrement: () => state.count--,
|
|
81
|
+
}),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Use in components
|
|
85
|
+
const { count } = counterStore.useSnapshot();
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Numbro
|
|
89
|
+
|
|
90
|
+
BigNumber.js wrapper for precise number formatting and calculations.
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { numbro } from "@dune2/tools/numbro";
|
|
94
|
+
|
|
95
|
+
const price = numbro(123.456);
|
|
96
|
+
price.format({ mantissa: 2 }); // "123.46"
|
|
97
|
+
price.formatCurrency({ symbol: "$" }); // "$123.46"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Shared
|
|
101
|
+
|
|
102
|
+
TypeScript utility types for better type safety.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import type { OptionalKeys, Overwrite, Print } from "@dune2/tools/shared";
|
|
106
|
+
```
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { QueryClient } from "@tanstack/react-query";
|
|
2
2
|
|
|
3
|
-
import { queryClient } from "./defaultQueryClient";
|
|
4
3
|
import type { Basic, RequestBuilderOptions, RequestConfig } from "./options";
|
|
5
4
|
|
|
6
5
|
export class RequestBuilder<Req = any, Res = any> {
|
|
@@ -19,7 +18,7 @@ export class RequestBuilder<Req = any, Res = any> {
|
|
|
19
18
|
//#endregion
|
|
20
19
|
|
|
21
20
|
// 这是默认的 Query Client 实例
|
|
22
|
-
static queryClient: QueryClient | null =
|
|
21
|
+
static queryClient: QueryClient | null = null;
|
|
23
22
|
static setQueryClient(queryClient: QueryClient | null) {
|
|
24
23
|
RequestBuilder.queryClient = queryClient;
|
|
25
24
|
}
|
package/src/rq/options.ts
CHANGED
|
@@ -5,10 +5,34 @@ import type {
|
|
|
5
5
|
useQuery,
|
|
6
6
|
} from "@tanstack/react-query";
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
// 定义 HTTP 方法类型
|
|
9
|
+
export type HttpMethod =
|
|
10
|
+
| "get"
|
|
11
|
+
| "post"
|
|
12
|
+
| "put"
|
|
13
|
+
| "delete"
|
|
14
|
+
| "patch"
|
|
15
|
+
| "head"
|
|
16
|
+
| "options";
|
|
9
17
|
// 外部可以重写这个类型
|
|
10
18
|
export interface RequestBuilderMeta {}
|
|
11
19
|
|
|
20
|
+
// 通用的请求配置接口,不依赖于具体的 HTTP 库
|
|
21
|
+
export interface RequestFnParams {
|
|
22
|
+
/** 请求URL */
|
|
23
|
+
url?: string;
|
|
24
|
+
/** HTTP 请求方法 */
|
|
25
|
+
method?: string;
|
|
26
|
+
/** URL 查询参数 */
|
|
27
|
+
params?: any;
|
|
28
|
+
/** 请求体数据 */
|
|
29
|
+
data?: any;
|
|
30
|
+
/** 请求头 */
|
|
31
|
+
headers?: Record<string, string>;
|
|
32
|
+
/** 请求取消 */
|
|
33
|
+
signal?: AbortSignal;
|
|
34
|
+
}
|
|
35
|
+
|
|
12
36
|
export interface Basic {
|
|
13
37
|
/**
|
|
14
38
|
* 请求方法
|
|
@@ -16,7 +40,7 @@ export interface Basic {
|
|
|
16
40
|
* 2. 如果在调用`request/useQuery`之类的不想用实例化是传入的`requestFn`,可以在`request/useQuery`的第二个参数传入`requestFn`
|
|
17
41
|
* 一般场景是有些情况需要全局`toast`,有些场景不需要,所以在不同的场景下传入不同实现的`requestFn`
|
|
18
42
|
*/
|
|
19
|
-
requestFn?: <T = unknown>(
|
|
43
|
+
requestFn?: <T = unknown>(params: RequestFnParams) => Promise<T>;
|
|
20
44
|
|
|
21
45
|
meta?: RequestBuilderMeta;
|
|
22
46
|
}
|
|
@@ -45,7 +69,7 @@ export interface RequestBuilderOptions<Req, Res>
|
|
|
45
69
|
* 请求方法
|
|
46
70
|
* @default "get"
|
|
47
71
|
*/
|
|
48
|
-
method?:
|
|
72
|
+
method?: HttpMethod;
|
|
49
73
|
// 请求路径
|
|
50
74
|
url: string;
|
|
51
75
|
// url path 上的参数 , /prefunding-order/{id} 中的 id
|
|
@@ -58,7 +82,7 @@ export interface RequestBuilderOptions<Req, Res>
|
|
|
58
82
|
useMutationOptions?: UseMutationOptions<Res, unknown, Req>;
|
|
59
83
|
}
|
|
60
84
|
|
|
61
|
-
export type RequestConfig = Basic &
|
|
85
|
+
export type RequestConfig = Basic & RequestFnParams;
|
|
62
86
|
|
|
63
87
|
export type PageData<T = any> = {
|
|
64
88
|
total?: number;
|
package/src/logger/Logger.ts
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { noop } from "es-toolkit";
|
|
2
|
-
import store2 from "store2";
|
|
3
|
-
import { Level, type OnLogParams } from "./shared";
|
|
4
|
-
|
|
5
|
-
type StorageState = Record<string, Level>;
|
|
6
|
-
|
|
7
|
-
export type OnLog = (data: OnLogParams) => void;
|
|
8
|
-
|
|
9
|
-
export class Logger {
|
|
10
|
-
constructor(
|
|
11
|
-
public name: string,
|
|
12
|
-
public storageKey?: string,
|
|
13
|
-
/**
|
|
14
|
-
* 当前启用的等级
|
|
15
|
-
* 如果设置为 Silent 则不输出任何日志
|
|
16
|
-
* 只有大于等于当前等级的日志才会输出
|
|
17
|
-
*/
|
|
18
|
-
public level = Level.Debug,
|
|
19
|
-
public onLog: OnLog = noop,
|
|
20
|
-
) {
|
|
21
|
-
this.level = this.getStorageLevel() ?? level;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
private getStorageLevel() {
|
|
25
|
-
if (this.storageKey) {
|
|
26
|
-
const state = store2.get(this.storageKey) as StorageState;
|
|
27
|
-
return state?.[this.name];
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
private setStorageLevel(level: Level) {
|
|
31
|
-
if (this.storageKey) {
|
|
32
|
-
const state = store2.get(this.storageKey) as StorageState;
|
|
33
|
-
store2.set(this.storageKey, { ...state, [this.name]: level });
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
setLevel(level: Level) {
|
|
37
|
-
this.level = level;
|
|
38
|
-
this.setStorageLevel(level);
|
|
39
|
-
}
|
|
40
|
-
enable() {
|
|
41
|
-
this.setLevel(Level.Debug);
|
|
42
|
-
}
|
|
43
|
-
disable() {
|
|
44
|
-
this.setLevel(Level.Error);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// 是否应该输出日志
|
|
48
|
-
private shouldLog(level: Level) {
|
|
49
|
-
return level >= this.level;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
private combineArgs(level: Level, tag: string, args: any[]): OnLogParams {
|
|
53
|
-
const data = {
|
|
54
|
-
name: this.name,
|
|
55
|
-
level,
|
|
56
|
-
msg: args,
|
|
57
|
-
tag,
|
|
58
|
-
time: Date.now(),
|
|
59
|
-
shouldLog: this.shouldLog(level),
|
|
60
|
-
};
|
|
61
|
-
return data;
|
|
62
|
-
}
|
|
63
|
-
private formatArgs(data: OnLogParams) {
|
|
64
|
-
const time = new Date(data.time).toLocaleTimeString("zh-CN");
|
|
65
|
-
const prefix = `[${time}] ${data.tag} ${data.name}: `;
|
|
66
|
-
|
|
67
|
-
const first = data.msg[0];
|
|
68
|
-
|
|
69
|
-
if (typeof first === "string") {
|
|
70
|
-
// 确保 %o %O 等占位符正常工作
|
|
71
|
-
return [`${prefix}${first}`, ...data.msg.slice(1)];
|
|
72
|
-
}
|
|
73
|
-
return [`${prefix}`, ...data.msg];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
debug(...args: any[]) {
|
|
77
|
-
const data = this.combineArgs(Level.Debug, "DEBUG", args);
|
|
78
|
-
this.onLog(data);
|
|
79
|
-
if (data.shouldLog) {
|
|
80
|
-
console.debug(...this.formatArgs(data));
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
info(...args: any[]) {
|
|
84
|
-
const data = this.combineArgs(Level.Info, "INFO", args);
|
|
85
|
-
this.onLog(data);
|
|
86
|
-
if (data.shouldLog) {
|
|
87
|
-
console.log(...this.formatArgs(data));
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
warn(...args: any[]) {
|
|
91
|
-
const data = this.combineArgs(Level.Warn, "WARN", args);
|
|
92
|
-
this.onLog(data);
|
|
93
|
-
if (data.shouldLog) {
|
|
94
|
-
console.warn(...this.formatArgs(data));
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
error(...args: any[]) {
|
|
98
|
-
const data = this.combineArgs(Level.Error, "ERROR", args);
|
|
99
|
-
this.onLog(data);
|
|
100
|
-
if (data.shouldLog) {
|
|
101
|
-
console.error(...this.formatArgs(data));
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
package/src/logger/index.ts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { camelCase } from "es-toolkit";
|
|
2
|
-
import { forEach } from "es-toolkit/compat";
|
|
3
|
-
import { Logger, type OnLog } from "./Logger";
|
|
4
|
-
import { Level } from "./shared";
|
|
5
|
-
|
|
6
|
-
interface Config<Name extends string = string> {
|
|
7
|
-
// 创建的 logger 名称
|
|
8
|
-
loggers: Name[];
|
|
9
|
-
// 默认日志级别
|
|
10
|
-
level?: Level;
|
|
11
|
-
// 日志回调,用于上报日志
|
|
12
|
-
onLog?: OnLog;
|
|
13
|
-
// 存储到本地的 key,用于存储日志级别
|
|
14
|
-
storageKey?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
type NormalizeName<T extends string> = T extends `${infer A}.${infer B}`
|
|
18
|
-
? `${A}${Capitalize<NormalizeName<B>>}`
|
|
19
|
-
: T;
|
|
20
|
-
|
|
21
|
-
export function createLogger<Name extends string>(config: Config<Name>) {
|
|
22
|
-
type Key = NormalizeName<Name>;
|
|
23
|
-
|
|
24
|
-
// 内置关键字
|
|
25
|
-
const keywords = ["debug", "info", "warn", "error", "enable", "disable"];
|
|
26
|
-
|
|
27
|
-
const loggerMap = {} as Record<Key, Logger>;
|
|
28
|
-
config.loggers.forEach((name) => {
|
|
29
|
-
if (keywords.includes(name)) {
|
|
30
|
-
throw new Error(`logger name can't be ${name}`);
|
|
31
|
-
}
|
|
32
|
-
// 输入:foo.login
|
|
33
|
-
// 输出:fooLogin
|
|
34
|
-
const key = (name.includes(".") ? camelCase(name) : name) as Key;
|
|
35
|
-
|
|
36
|
-
if (loggerMap[key]) {
|
|
37
|
-
throw new Error(`logger name "${name}" is duplicated`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const logger = new Logger(
|
|
41
|
-
name,
|
|
42
|
-
config.storageKey,
|
|
43
|
-
config.level ?? Level.Debug,
|
|
44
|
-
config.onLog,
|
|
45
|
-
);
|
|
46
|
-
loggerMap[key] = logger;
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
const method = {
|
|
50
|
-
enable() {
|
|
51
|
-
forEach(loggerMap, (v) => {
|
|
52
|
-
v.enable();
|
|
53
|
-
});
|
|
54
|
-
},
|
|
55
|
-
disable() {
|
|
56
|
-
forEach(loggerMap, (v) => {
|
|
57
|
-
v.disable();
|
|
58
|
-
});
|
|
59
|
-
},
|
|
60
|
-
setLevel(level: Level) {
|
|
61
|
-
forEach(loggerMap, (v) => {
|
|
62
|
-
v.setLevel(level);
|
|
63
|
-
});
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
...method,
|
|
69
|
-
...loggerMap,
|
|
70
|
-
};
|
|
71
|
-
}
|
package/src/logger/shared.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export enum Level {
|
|
2
|
-
Debug,
|
|
3
|
-
Info,
|
|
4
|
-
Warn,
|
|
5
|
-
Error,
|
|
6
|
-
Silent,
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export type OnLogParams = {
|
|
10
|
-
// 日志等级
|
|
11
|
-
level: Level;
|
|
12
|
-
// 可读的日志等级
|
|
13
|
-
tag: string;
|
|
14
|
-
// 用于标识日志的名称
|
|
15
|
-
name: string;
|
|
16
|
-
// 日志输出时间
|
|
17
|
-
time: number;
|
|
18
|
-
// 是否应该输出日志
|
|
19
|
-
shouldLog: boolean;
|
|
20
|
-
// 日志内容
|
|
21
|
-
msg: any[];
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
// 让 vite 能识别成 es-module
|
|
25
|
-
export default {};
|