rxtutils 1.1.5 → 1.1.7

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/README.md CHANGED
@@ -12,6 +12,12 @@ yarn add rxtutils
12
12
  pnpm add rxtutils
13
13
  ```
14
14
 
15
+ 也支持按需导入子模块(如 `rxtutils/cache`, `rxtutils/hooks` 等)。
16
+
17
+ ### ⚠️ 环境要求
18
+
19
+ 本库的构建目标为 **ES2020**。如果在低版本环境中使用,请确保配置了相应的 Polyfill 或构建转译。
20
+
15
21
  ## 🚀 功能特性
16
22
 
17
23
  - **缓存管理** - 支持内存、localStorage、sessionStorage 和 IndexedDB 多种存储方式
@@ -67,7 +73,7 @@ const customCache = new Cache<{ id: number; name: string }, any>(
67
73
  'customCache',
68
74
  300,
69
75
  'customDB',
70
- (prev, next) => prev.id === next.id // 只比较 id 字段
76
+ (prev, next) => prev.id === next.id, // 只比较 id 字段
71
77
  );
72
78
  ```
73
79
 
@@ -84,13 +90,13 @@ import { createBaseRequest } from 'rxtutils';
84
90
  const request = createBaseRequest({
85
91
  baseURL: 'https://api.example.com',
86
92
  throwError: true,
87
- defaultMessageShower: (message) => console.log(message)
93
+ defaultMessageShower: (message) => console.log(message),
88
94
  });
89
95
 
90
96
  // 创建具体请求
91
97
  const getUserInfo = request<{ id: number }, { name: string; email: string }>({
92
98
  method: 'GET',
93
- url: '/user/:id'
99
+ url: '/user/:id',
94
100
  });
95
101
 
96
102
  // 发送请求
@@ -107,13 +113,13 @@ const request = createBaseRequest({
107
113
  '401': '未授权,请重新登录',
108
114
  '500': (code, data, res) => ({
109
115
  replaceResData: { error: '服务器内部错误' },
110
- throwError: false
111
- })
116
+ throwError: false,
117
+ }),
112
118
  },
113
119
  httpErrorCodeMap: {
114
120
  404: '资源不存在',
115
- 500: '服务器错误'
116
- }
121
+ 500: '服务器错误',
122
+ },
117
123
  });
118
124
  ```
119
125
 
@@ -126,7 +132,7 @@ const request = createBaseRequest({
126
132
  cacheData: true,
127
133
  cacheDataInStorage: 'localStorage',
128
134
  cacheDataKey: 'api-cache',
129
- cacheTime: 300
135
+ cacheTime: 300,
130
136
  });
131
137
 
132
138
  // 第一次请求会从服务器获取数据
@@ -158,7 +164,7 @@ const userStore = createStateStore({
158
164
  // 在组件中使用
159
165
  function UserComponent() {
160
166
  const [user, setUser] = userStore.use();
161
-
167
+
162
168
  const handleLogin = () => {
163
169
  setUser({
164
170
  name: 'John Doe',
@@ -166,7 +172,7 @@ function UserComponent() {
166
172
  isLoggedIn: true
167
173
  });
168
174
  };
169
-
175
+
170
176
  return (
171
177
  <div>
172
178
  <p>用户名: {user.name}</p>
@@ -274,12 +280,10 @@ console.log(allErrors); // 返回所有验证错误
274
280
  import { BaseValidator } from 'rxtutils';
275
281
 
276
282
  // 创建自定义验证装饰器
277
- const VCustom = BaseValidator.decoratorCreator(
278
- (val) => {
279
- // 自定义验证逻辑
280
- return typeof val === 'string' && val.startsWith('custom-');
281
- }
282
- );
283
+ const VCustom = BaseValidator.decoratorCreator((val) => {
284
+ // 自定义验证逻辑
285
+ return typeof val === 'string' && val.startsWith('custom-');
286
+ });
283
287
 
284
288
  // 使用自定义验证装饰器
285
289
  class Product extends BaseValidator {
@@ -329,7 +333,7 @@ form.password = '123456';
329
333
 
330
334
  // 验证单个字段的所有规则
331
335
  const usernameErrors = form.validate('username', true);
332
- console.log(usernameErrors);
336
+ console.log(usernameErrors);
333
337
  // [{ status: false, message: '用户名长度不能少于3位' }]
334
338
 
335
339
  // 验证所有字段,每个字段遇到第一个错误就停止
@@ -379,7 +383,7 @@ const useUserGetters = createStoreGetterMemo(userStore, getters, getterNameMaps)
379
383
 
380
384
  function UserProfile() {
381
385
  const { fullName, isAdult, displayName } = useUserGetters();
382
-
386
+
383
387
  return (
384
388
  <div>
385
389
  <h1>{fullName}</h1>
@@ -394,26 +398,26 @@ function UserProfile() {
394
398
 
395
399
  ### Cache 配置
396
400
 
397
- | 参数 | 类型 | 默认值 | 说明 |
398
- |------|------|--------|------|
399
- | `storageType` | `'sessionStorage' \| 'localStorage' \| 'indexedDB'` | `undefined` | 存储类型 |
400
- | `cacheKey` | `string` | `undefined` | 缓存键名 |
401
- | `cacheTime` | `number` | `60` | 缓存时间(秒) |
402
- | `indexDBName` | `string` | `'__apiCacheDatabase__'` | IndexedDB 数据库名称 |
403
- | `cacheKeyEquals` | `function` | `defaultEquals` | 缓存键比较函数 |
401
+ | 参数 | 类型 | 默认值 | 说明 |
402
+ | ---------------- | --------------------------------------------------- | ------------------------ | -------------------- |
403
+ | `storageType` | `'sessionStorage' \| 'localStorage' \| 'indexedDB'` | `undefined` | 存储类型 |
404
+ | `cacheKey` | `string` | `undefined` | 缓存键名 |
405
+ | `cacheTime` | `number` | `60` | 缓存时间(秒) |
406
+ | `indexDBName` | `string` | `'__apiCacheDatabase__'` | IndexedDB 数据库名称 |
407
+ | `cacheKeyEquals` | `function` | `defaultEquals` | 缓存键比较函数 |
404
408
 
405
409
  ### Request 配置
406
410
 
407
- | 参数 | 类型 | 默认值 | 说明 |
408
- |------|------|--------|------|
409
- | `baseURL` | `string` | `''` | 请求基础URL |
410
- | `throwError` | `boolean` | `true` | 是否抛出错误 |
411
- | `enableCache` | `boolean` | `false` | 是否启用缓存 |
412
- | `cacheData` | `boolean` | `false` | 是否缓存数据 |
413
- | `cacheTime` | `number` | `60` | 缓存时间(秒) |
414
- | `cacheDataInStorage` | `StorageType` | `undefined` | 缓存存储类型 |
415
- | `errorCodePath` | `string` | `'code'` | 错误码路径 |
416
- | `successCodes` | `string[]` | `['0', '200']` | 成功状态码 |
411
+ | 参数 | 类型 | 默认值 | 说明 |
412
+ | -------------------- | ------------- | -------------- | -------------- |
413
+ | `baseURL` | `string` | `''` | 请求基础URL |
414
+ | `throwError` | `boolean` | `true` | 是否抛出错误 |
415
+ | `enableCache` | `boolean` | `false` | 是否启用缓存 |
416
+ | `cacheData` | `boolean` | `false` | 是否缓存数据 |
417
+ | `cacheTime` | `number` | `60` | 缓存时间(秒) |
418
+ | `cacheDataInStorage` | `StorageType` | `undefined` | 缓存存储类型 |
419
+ | `errorCodePath` | `string` | `'code'` | 错误码路径 |
420
+ | `successCodes` | `string[]` | `['0', '200']` | 成功状态码 |
417
421
 
418
422
  ## 📝 类型定义
419
423
 
@@ -487,12 +491,12 @@ const apiRequest = createBaseRequest({
487
491
  enableCache: true,
488
492
  cacheData: true,
489
493
  cacheDataInStorage: 'localStorage',
490
- cacheTime: 300
494
+ cacheTime: 300,
491
495
  });
492
496
 
493
497
  const getProductList = apiRequest<{ page: number }, { products: Product[] }>({
494
498
  method: 'GET',
495
- url: '/products'
499
+ url: '/products',
496
500
  });
497
501
  ```
498
502
 
@@ -503,19 +507,23 @@ const getProductList = apiRequest<{ page: number }, { products: Product[] }>({
503
507
  const userStore = createStateStore({
504
508
  user: null,
505
509
  permissions: [],
506
- theme: 'light'
510
+ theme: 'light',
507
511
  });
508
512
 
509
513
  // 创建用户相关的计算属性
510
- const userGetters = createStoreGetter(userStore, {
511
- isLoggedIn: (state) => !!state.user,
512
- canEdit: (state) => state.permissions.includes('edit'),
513
- isDarkTheme: (state) => state.theme === 'dark'
514
- }, {
515
- isLoggedIn: 'isLoggedIn',
516
- canEdit: 'canEdit',
517
- isDarkTheme: 'isDarkTheme'
518
- });
514
+ const userGetters = createStoreGetter(
515
+ userStore,
516
+ {
517
+ isLoggedIn: (state) => !!state.user,
518
+ canEdit: (state) => state.permissions.includes('edit'),
519
+ isDarkTheme: (state) => state.theme === 'dark',
520
+ },
521
+ {
522
+ isLoggedIn: 'isLoggedIn',
523
+ canEdit: 'canEdit',
524
+ isDarkTheme: 'isDarkTheme',
525
+ },
526
+ );
519
527
  ```
520
528
 
521
529
  ### 3. 表单数据缓存
@@ -531,9 +539,69 @@ formCache.setCache('user-form', formData);
531
539
  const savedData = formCache.getCache('user-form');
532
540
  ```
533
541
 
542
+ ## 🛠 开发
543
+
544
+ 本项目包含一个 playground 用于调试和预览。
545
+
546
+ ```bash
547
+ # 安装依赖
548
+ pnpm install --frozen-lockfile
549
+
550
+ # 启动开发服务器
551
+ pnpm dev
552
+
553
+ # 运行测试
554
+ pnpm test
555
+
556
+ # 代码检查
557
+ pnpm lint
558
+
559
+ # 代码格式化
560
+ pnpm format
561
+
562
+ # 类型检查
563
+ pnpm typecheck
564
+
565
+ # 构建
566
+ pnpm build
567
+ ```
568
+
534
569
  ## 🤝 贡献
535
570
 
536
- 欢迎提交 Issue 和 Pull Request 来帮助改进这个项目。
571
+ 欢迎提交 Issue 和 Pull Request 来帮助改进这个项目。提交前建议先执行:
572
+
573
+ ```bash
574
+ pnpm format:check
575
+ pnpm lint
576
+ pnpm typecheck
577
+ pnpm test
578
+ pnpm build
579
+ ```
580
+
581
+ ## 🔧 Git Alias(可选)
582
+
583
+ 如果你习惯使用 Git alias,建议配置到个人环境(`~/.gitconfig`),而不是放在仓库中。
584
+
585
+ 方式一:逐条命令配置
586
+
587
+ ```bash
588
+ git config --global alias.cm commit
589
+ git config --global alias.cc checkout
590
+ git config --global alias.bc branch
591
+ git config --global alias.df diff
592
+ git config --global alias.lg log
593
+ ```
594
+
595
+ 方式二:直接追加配置片段到 `~/.gitconfig`
596
+
597
+ ```ini
598
+ [alias]
599
+ cm = commit
600
+ cc = checkout
601
+ bc = branch
602
+ df = diff
603
+ lg = log
604
+ ```
537
605
 
538
606
  ## 📄 许可证
539
607
 
@@ -544,4 +612,4 @@ MIT License
544
612
  - [TypeScript](https://www.typescriptlang.org/)
545
613
  - [React](https://reactjs.org/)
546
614
  - [Axios](https://axios-http.com/)
547
- - [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API)
615
+ - [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API)
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
- const moment = require("moment");
3
+ const dateFns = require("date-fns");
4
4
  const indexDB = require("./indexDB.cjs");
5
5
  const defaultEquals = require("../_utils/defaultEquals.cjs");
6
6
  const StorageMap = {
@@ -40,17 +40,13 @@ class Cache {
40
40
  async _init() {
41
41
  const { storageType: cacheType, cacheKey } = this.cacheOptions;
42
42
  if (this.storage instanceof indexDB.IndexedDBStorage) {
43
- this.cache = JSON.parse(
44
- await this.storage.getItem(cacheKey) || "[]"
45
- );
43
+ this.cache = JSON.parse(await this.storage.getItem(cacheKey) || "[]");
46
44
  } else if (this.storage instanceof Storage) {
47
45
  this.storage = StorageMap[cacheType];
48
46
  if (this.storage) {
49
47
  if (typeof cacheKey === "string") {
50
48
  try {
51
- this.cache = JSON.parse(
52
- this.storage.getItem(cacheKey) || "[]"
53
- );
49
+ this.cache = JSON.parse(this.storage.getItem(cacheKey) || "[]");
54
50
  } catch (e) {
55
51
  this.cache = [];
56
52
  console.error(`缓存数据解析失败,key:${cacheKey}`);
@@ -68,7 +64,7 @@ class Cache {
68
64
  */
69
65
  _filterExpired() {
70
66
  const newCache = this.cache.filter((item) => {
71
- return moment(item.expireTime).isAfter(moment());
67
+ return dateFns.isAfter(dateFns.parseISO(item.expireTime), /* @__PURE__ */ new Date());
72
68
  });
73
69
  this.cache = newCache;
74
70
  }
@@ -80,10 +76,7 @@ class Cache {
80
76
  _saveToStorage() {
81
77
  if (this.storage) {
82
78
  if (typeof this.cacheOptions.cacheKey === "string") {
83
- this.storage.setItem(
84
- this.cacheOptions.cacheKey,
85
- JSON.stringify(this.cache)
86
- );
79
+ this.storage.setItem(this.cacheOptions.cacheKey, JSON.stringify(this.cache));
87
80
  }
88
81
  }
89
82
  }
@@ -107,7 +100,7 @@ class Cache {
107
100
  this.cache.push({
108
101
  params,
109
102
  data,
110
- expireTime: moment().add(cacheTime, "seconds").toJSON()
103
+ expireTime: dateFns.addSeconds(/* @__PURE__ */ new Date(), cacheTime ?? 0).toISOString()
111
104
  });
112
105
  this._saveToStorage();
113
106
  }
@@ -122,7 +115,7 @@ class Cache {
122
115
  });
123
116
  const item = this.cache[itemIndex];
124
117
  if (item) {
125
- if (moment(item.expireTime).isAfter(moment())) {
118
+ if (dateFns.isAfter(dateFns.parseISO(item.expireTime), /* @__PURE__ */ new Date())) {
126
119
  return item.data;
127
120
  } else {
128
121
  this.cache.splice(itemIndex, 1);
@@ -10,16 +10,19 @@ function useCombineControlValue({
10
10
  const { [valueKey]: value } = props;
11
11
  const hasValue = Object.prototype.hasOwnProperty.call(props, valueKey);
12
12
  const [internalValue, setInternalValue] = react.useState(value ?? defaultValue);
13
- const handleChange = react.useCallback((...params) => {
14
- let realNextVal;
15
- if (typeof resolveFn === "function") {
16
- realNextVal = resolveFn(...params);
17
- } else {
18
- realNextVal = params[0];
19
- }
20
- setInternalValue(realNextVal);
21
- onChange?.(...params);
22
- }, [onChange, resolveFn]);
13
+ const handleChange = react.useCallback(
14
+ (...params) => {
15
+ let realNextVal;
16
+ if (typeof resolveFn === "function") {
17
+ realNextVal = resolveFn(...params);
18
+ } else {
19
+ realNextVal = params[0];
20
+ }
21
+ setInternalValue(realNextVal);
22
+ onChange?.(...params);
23
+ },
24
+ [onChange, resolveFn]
25
+ );
23
26
  const finalValue = react.useMemo(() => {
24
27
  if (hasValue) return value;
25
28
  return internalValue;
@@ -37,19 +37,25 @@ function createBaseRequest(baseOptions) {
37
37
  ...createOptions,
38
38
  ...options
39
39
  };
40
- let { requestMiddlewares = [], axiosOptions: finalAxiosOptions = {}, requestParamsOrDataTransfer, responseTransfer } = mergedOptions;
40
+ let {
41
+ requestMiddlewares = [],
42
+ axiosOptions: finalAxiosOptions = {},
43
+ requestParamsOrDataTransfer,
44
+ responseTransfer
45
+ } = mergedOptions;
41
46
  let finalRequestOptions = { ...requestOptions, ...requestParam };
42
47
  for (const middleware of requestMiddlewares) {
43
- const { axiosOptions: nextAxiosOptions = finalAxiosOptions, requestOptions: nextRequestOptions = finalRequestOptions } = await middleware({ ...mergedOptions, axiosOptions: finalAxiosOptions }, finalRequestOptions);
48
+ const {
49
+ axiosOptions: nextAxiosOptions = finalAxiosOptions,
50
+ requestOptions: nextRequestOptions = finalRequestOptions
51
+ } = await middleware(
52
+ { ...mergedOptions, axiosOptions: finalAxiosOptions },
53
+ finalRequestOptions
54
+ );
44
55
  finalAxiosOptions = nextAxiosOptions;
45
56
  finalRequestOptions = nextRequestOptions;
46
57
  }
47
- const {
48
- method: method2,
49
- url: url2,
50
- data = {},
51
- params = {}
52
- } = finalRequestOptions;
58
+ const { method: method2, url: url2, data = {}, params = {} } = finalRequestOptions;
53
59
  let requestDataOrParams = params;
54
60
  if (method2.toLowerCase() === "post") {
55
61
  requestDataOrParams = data;
@@ -62,18 +68,9 @@ function createBaseRequest(baseOptions) {
62
68
  const {
63
69
  enableCache = false,
64
70
  cacheData = false,
65
- defaultErrorCodeHandler = defaultHandlers._defaultErrorCodeHandler.bind(
66
- null,
67
- defaultMessageShower
68
- ),
69
- defaultHttpErrorCodeHandler = defaultHandlers._defaultHttpErrorCodeHandler.bind(
70
- null,
71
- defaultMessageShower
72
- ),
73
- otherErrorHandler = defaultHandlers._defaultOtherErrorCodeHandler.bind(
74
- null,
75
- defaultMessageShower
76
- ),
71
+ defaultErrorCodeHandler = defaultHandlers._defaultErrorCodeHandler.bind(null, defaultMessageShower),
72
+ defaultHttpErrorCodeHandler = defaultHandlers._defaultHttpErrorCodeHandler.bind(null, defaultMessageShower),
73
+ otherErrorHandler = defaultHandlers._defaultOtherErrorCodeHandler.bind(null, defaultMessageShower),
77
74
  errorCodePath = "code",
78
75
  cacheTime: cacheTime2 = 60,
79
76
  errorCodeMap = {},
@@ -98,8 +95,8 @@ function createBaseRequest(baseOptions) {
98
95
  return instance.request({
99
96
  method: method2,
100
97
  url: url2,
101
- data: data ?? (requestParamsOrDataTransfer ? requestParamsOrDataTransfer(data) : data),
102
- params: params ?? (requestParamsOrDataTransfer ? requestParamsOrDataTransfer(params) : params),
98
+ data: requestParamsOrDataTransfer ? requestParamsOrDataTransfer(data) : data,
99
+ params: requestParamsOrDataTransfer ? requestParamsOrDataTransfer(params) : params,
103
100
  ...finalAxiosOptions
104
101
  }).then(
105
102
  async (res) => {
@@ -118,10 +115,7 @@ function createBaseRequest(baseOptions) {
118
115
  const retryTask = retry();
119
116
  if (retryTask) return retryTask;
120
117
  } else {
121
- const {
122
- replaceResData = res.data,
123
- throwError: handlerThrowError = "default"
124
- } = Object(
118
+ const { replaceResData = res.data, throwError: handlerThrowError = "default" } = Object(
125
119
  await customHandler(errorCode, res.data, res, {
126
120
  ...requestOptions,
127
121
  ...requestParam
@@ -145,19 +139,14 @@ function createBaseRequest(baseOptions) {
145
139
  async (error$1) => {
146
140
  if (error$1.response) {
147
141
  let resData = error$1;
148
- const {
149
- [error$1.response.status]: customHandler = defaultHttpErrorCodeHandler
150
- } = httpErrorCodeMap;
142
+ const { [error$1.response.status]: customHandler = defaultHttpErrorCodeHandler } = httpErrorCodeMap;
151
143
  const err = new error.default("服务端错误", "http", error$1);
152
144
  if (typeof customHandler === "string") {
153
145
  defaultMessageShower(customHandler);
154
146
  const retryTask = retry();
155
147
  if (retryTask) return retryTask;
156
148
  } else {
157
- const {
158
- replaceResData = error$1,
159
- throwError: handlerThrowError = "default"
160
- } = Object(
149
+ const { replaceResData = error$1, throwError: handlerThrowError = "default" } = Object(
161
150
  await customHandler(error$1.response.status, error$1, {
162
151
  ...requestOptions,
163
152
  ...requestParam
@@ -182,12 +171,7 @@ function createBaseRequest(baseOptions) {
182
171
  const err = new error.default("服务端错误", "http", error$1);
183
172
  err.type = "http";
184
173
  err.data = error$1;
185
- const {
186
- replaceResData = error$1,
187
- throwError: handlerThrowError = "default"
188
- } = Object(
189
- await otherErrorHandler(error$1)
190
- );
174
+ const { replaceResData = error$1, throwError: handlerThrowError = "default" } = Object(await otherErrorHandler(error$1));
191
175
  const retryTask = retry();
192
176
  if (retryTask) return retryTask;
193
177
  resData = replaceResData;
@@ -9,38 +9,30 @@ function VRequired(noneVals = [void 0]) {
9
9
  return true;
10
10
  });
11
11
  }
12
- const VString = validator.BaseValidator.decoratorCreator(
13
- (val) => {
14
- if (typeof val !== "string") {
15
- return false;
16
- }
17
- return true;
12
+ const VString = validator.BaseValidator.decoratorCreator((val) => {
13
+ if (typeof val !== "string") {
14
+ return false;
18
15
  }
19
- );
20
- const VNumber = validator.BaseValidator.decoratorCreator(
21
- (val) => {
22
- if (typeof val !== "number") {
23
- return false;
24
- }
25
- return true;
16
+ return true;
17
+ });
18
+ const VNumber = validator.BaseValidator.decoratorCreator((val) => {
19
+ if (typeof val !== "number") {
20
+ return false;
26
21
  }
27
- );
28
- const VArray = validator.BaseValidator.decoratorCreator(
29
- (val) => {
30
- if (!Array.isArray(val)) {
31
- return false;
32
- }
33
- return true;
22
+ return true;
23
+ });
24
+ const VArray = validator.BaseValidator.decoratorCreator((val) => {
25
+ if (!Array.isArray(val)) {
26
+ return false;
34
27
  }
35
- );
36
- const VBoolean = validator.BaseValidator.decoratorCreator(
37
- (val) => {
38
- if (typeof val !== "boolean") {
39
- return false;
40
- }
41
- return true;
28
+ return true;
29
+ });
30
+ const VBoolean = validator.BaseValidator.decoratorCreator((val) => {
31
+ if (typeof val !== "boolean") {
32
+ return false;
42
33
  }
43
- );
34
+ return true;
35
+ });
44
36
  const VMin = (min) => validator.BaseValidator.decoratorCreator((val) => {
45
37
  if (typeof val !== "number" || val < min) {
46
38
  return false;
@@ -71,15 +63,13 @@ const VMaxLength = (maxLen) => validator.BaseValidator.decoratorCreator((val) =>
71
63
  }
72
64
  return true;
73
65
  });
74
- const VEmail = validator.BaseValidator.decoratorCreator(
75
- (val) => {
76
- if (typeof val !== "string") {
77
- return false;
78
- }
79
- const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
80
- return emailReg.test(val);
66
+ const VEmail = validator.BaseValidator.decoratorCreator((val) => {
67
+ if (typeof val !== "string") {
68
+ return false;
81
69
  }
82
- );
70
+ const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
71
+ return emailReg.test(val);
72
+ });
83
73
  const VPattern = (pattern) => validator.BaseValidator.decoratorCreator((val) => {
84
74
  if (typeof val !== "string") {
85
75
  return false;
@@ -5,7 +5,7 @@ import { IndexedDBStorage } from './indexDB';
5
5
  * - localStorage: 本地存储,永久保存
6
6
  * - indexedDB: IndexedDB 数据库存储
7
7
  */
8
- export type StorageType = "sessionStorage" | "localStorage" | "indexedDB";
8
+ export type StorageType = 'sessionStorage' | 'localStorage' | 'indexedDB';
9
9
  /**
10
10
  * 缓存项接口定义
11
11
  * 定义了单个缓存项的数据结构
@@ -27,7 +27,7 @@ export interface ICache<Param, Data> {
27
27
  /**
28
28
  * 过期时间
29
29
  * - ISO 8601 格式的字符串
30
- * - 由 moment().add(cacheTime, 'seconds').toJSON() 生成
30
+ * - 由 addSeconds(new Date(), cacheTime).toISOString() 生成
31
31
  * - 示例:'2025-06-12T10:30:00.000Z'
32
32
  */
33
33
  expireTime: string;
@@ -122,7 +122,7 @@ export default class Cache<Param, Data> {
122
122
  * @param data 要缓存的数据
123
123
  * @param cacheOptions 可选的缓存配置,可以覆盖默认的缓存时间
124
124
  */
125
- setCache(params: Param, data: Data, cacheOptions?: Omit<ICacheOptions<Param>, "storageType" | "cacheKey" | "cacheKeyEquals">): void;
125
+ setCache(params: Param, data: Data, cacheOptions?: Omit<ICacheOptions<Param>, 'storageType' | 'cacheKey' | 'cacheKeyEquals'>): void;
126
126
  /**
127
127
  * 获取缓存数据
128
128
  * @param params 查询参数
@@ -1,4 +1,4 @@
1
- import moment from "moment";
1
+ import { isAfter, parseISO, addSeconds } from "date-fns";
2
2
  import { IndexedDBStorage } from "./indexDB.mjs";
3
3
  import defaultEquals from "../_utils/defaultEquals.mjs";
4
4
  const StorageMap = {
@@ -38,17 +38,13 @@ class Cache {
38
38
  async _init() {
39
39
  const { storageType: cacheType, cacheKey } = this.cacheOptions;
40
40
  if (this.storage instanceof IndexedDBStorage) {
41
- this.cache = JSON.parse(
42
- await this.storage.getItem(cacheKey) || "[]"
43
- );
41
+ this.cache = JSON.parse(await this.storage.getItem(cacheKey) || "[]");
44
42
  } else if (this.storage instanceof Storage) {
45
43
  this.storage = StorageMap[cacheType];
46
44
  if (this.storage) {
47
45
  if (typeof cacheKey === "string") {
48
46
  try {
49
- this.cache = JSON.parse(
50
- this.storage.getItem(cacheKey) || "[]"
51
- );
47
+ this.cache = JSON.parse(this.storage.getItem(cacheKey) || "[]");
52
48
  } catch (e) {
53
49
  this.cache = [];
54
50
  console.error(`缓存数据解析失败,key:${cacheKey}`);
@@ -66,7 +62,7 @@ class Cache {
66
62
  */
67
63
  _filterExpired() {
68
64
  const newCache = this.cache.filter((item) => {
69
- return moment(item.expireTime).isAfter(moment());
65
+ return isAfter(parseISO(item.expireTime), /* @__PURE__ */ new Date());
70
66
  });
71
67
  this.cache = newCache;
72
68
  }
@@ -78,10 +74,7 @@ class Cache {
78
74
  _saveToStorage() {
79
75
  if (this.storage) {
80
76
  if (typeof this.cacheOptions.cacheKey === "string") {
81
- this.storage.setItem(
82
- this.cacheOptions.cacheKey,
83
- JSON.stringify(this.cache)
84
- );
77
+ this.storage.setItem(this.cacheOptions.cacheKey, JSON.stringify(this.cache));
85
78
  }
86
79
  }
87
80
  }
@@ -105,7 +98,7 @@ class Cache {
105
98
  this.cache.push({
106
99
  params,
107
100
  data,
108
- expireTime: moment().add(cacheTime, "seconds").toJSON()
101
+ expireTime: addSeconds(/* @__PURE__ */ new Date(), cacheTime ?? 0).toISOString()
109
102
  });
110
103
  this._saveToStorage();
111
104
  }
@@ -120,7 +113,7 @@ class Cache {
120
113
  });
121
114
  const item = this.cache[itemIndex];
122
115
  if (item) {
123
- if (moment(item.expireTime).isAfter(moment())) {
116
+ if (isAfter(parseISO(item.expireTime), /* @__PURE__ */ new Date())) {
124
117
  return item.data;
125
118
  } else {
126
119
  this.cache.splice(itemIndex, 1);
@@ -8,16 +8,19 @@ function useCombineControlValue({
8
8
  const { [valueKey]: value } = props;
9
9
  const hasValue = Object.prototype.hasOwnProperty.call(props, valueKey);
10
10
  const [internalValue, setInternalValue] = useState(value ?? defaultValue);
11
- const handleChange = useCallback((...params) => {
12
- let realNextVal;
13
- if (typeof resolveFn === "function") {
14
- realNextVal = resolveFn(...params);
15
- } else {
16
- realNextVal = params[0];
17
- }
18
- setInternalValue(realNextVal);
19
- onChange?.(...params);
20
- }, [onChange, resolveFn]);
11
+ const handleChange = useCallback(
12
+ (...params) => {
13
+ let realNextVal;
14
+ if (typeof resolveFn === "function") {
15
+ realNextVal = resolveFn(...params);
16
+ } else {
17
+ realNextVal = params[0];
18
+ }
19
+ setInternalValue(realNextVal);
20
+ onChange?.(...params);
21
+ },
22
+ [onChange, resolveFn]
23
+ );
21
24
  const finalValue = useMemo(() => {
22
25
  if (hasValue) return value;
23
26
  return internalValue;
@@ -14,7 +14,7 @@ export type ErrorHandlerReturnType<D> = {
14
14
  * - false: 不抛出错误
15
15
  * - 'default': 使用默认错误处理逻辑
16
16
  */
17
- throwError?: boolean | "default";
17
+ throwError?: boolean | 'default';
18
18
  };
19
19
  /**
20
20
  * 请求配置选项接口
@@ -98,7 +98,7 @@ export interface Options<Params = any, Data = any> {
98
98
  * 可以配置 HTTP 状态码对应的错误信息或处理函数
99
99
  * @default {} 空对象,使用默认处理函数
100
100
  */
101
- httpErrorCodeMap?: Record<string, string | ((code: number, res: AxiosResponse<Data>, requestParam: RequestOptions<Params>) => (ErrorHandlerReturnType<Data> | void | Promise<ErrorHandlerReturnType<Data> | void>))>;
101
+ httpErrorCodeMap?: Record<string, string | ((code: number, res: AxiosResponse<Data>, requestParam: RequestOptions<Params>) => ErrorHandlerReturnType<Data> | void | Promise<ErrorHandlerReturnType<Data> | void>)>;
102
102
  /**
103
103
  * 默认 HTTP 错误码处理函数
104
104
  * 当 HTTP 状态码不在 httpErrorCodeMap 中时调用
@@ -109,12 +109,12 @@ export interface Options<Params = any, Data = any> {
109
109
  * 处理非 HTTP 错误和非业务错误码的错误
110
110
  */
111
111
  otherErrorHandler?: (error: any) => ErrorHandlerReturnType<Data> | void | Promise<ErrorHandlerReturnType<Data> | void>;
112
- axiosOptions?: Omit<AxiosRequestConfig<Params>, "method" | "url" | "params" | "data">;
112
+ axiosOptions?: Omit<AxiosRequestConfig<Params>, 'method' | 'url' | 'params' | 'data'>;
113
113
  requestMiddlewares?: ((options: Options<Params, Data>, requestOptions: RequestOptions<Params>) => Promise<{
114
- axiosOptions?: Options<Params, Data>["axiosOptions"];
114
+ axiosOptions?: Options<Params, Data>['axiosOptions'];
115
115
  requestOptions?: RequestOptions<Params>;
116
116
  }> | {
117
- axiosOptions: Options<Params, Data>["axiosOptions"];
117
+ axiosOptions: Options<Params, Data>['axiosOptions'];
118
118
  requestOptions?: RequestOptions<Params>;
119
119
  })[];
120
120
  retryTimes?: number;
@@ -152,7 +152,7 @@ export interface RequestOptions<Param> {
152
152
  * @param baseOptions 基础配置选项
153
153
  * @returns 请求创建函数
154
154
  */
155
- export default function createBaseRequest(baseOptions?: Options): <Param, Data extends Record<any, any>>(requestOptions: RequestOptions<Param>, createOptions?: Omit<Options<Param, Data>, "baseURL">) => {
155
+ export default function createBaseRequest<D extends Record<any, any>>(baseOptions?: Options<D>): <Param, Data extends Record<any, any>>(requestOptions: RequestOptions<Param>, createOptions?: Omit<Options<Param, Data>, "baseURL">) => {
156
156
  (requestParam?: Omit<RequestOptions<Param>, "url" | "method">, options?: Omit<Options<Param, Data>, "baseURL" | "cacheDataKey" | "cacheDataInStorage" | "cacheKeyEquals">): Promise<Data>;
157
157
  clearCache(): void;
158
158
  };
@@ -35,19 +35,25 @@ function createBaseRequest(baseOptions) {
35
35
  ...createOptions,
36
36
  ...options
37
37
  };
38
- let { requestMiddlewares = [], axiosOptions: finalAxiosOptions = {}, requestParamsOrDataTransfer, responseTransfer } = mergedOptions;
38
+ let {
39
+ requestMiddlewares = [],
40
+ axiosOptions: finalAxiosOptions = {},
41
+ requestParamsOrDataTransfer,
42
+ responseTransfer
43
+ } = mergedOptions;
39
44
  let finalRequestOptions = { ...requestOptions, ...requestParam };
40
45
  for (const middleware of requestMiddlewares) {
41
- const { axiosOptions: nextAxiosOptions = finalAxiosOptions, requestOptions: nextRequestOptions = finalRequestOptions } = await middleware({ ...mergedOptions, axiosOptions: finalAxiosOptions }, finalRequestOptions);
46
+ const {
47
+ axiosOptions: nextAxiosOptions = finalAxiosOptions,
48
+ requestOptions: nextRequestOptions = finalRequestOptions
49
+ } = await middleware(
50
+ { ...mergedOptions, axiosOptions: finalAxiosOptions },
51
+ finalRequestOptions
52
+ );
42
53
  finalAxiosOptions = nextAxiosOptions;
43
54
  finalRequestOptions = nextRequestOptions;
44
55
  }
45
- const {
46
- method: method2,
47
- url: url2,
48
- data = {},
49
- params = {}
50
- } = finalRequestOptions;
56
+ const { method: method2, url: url2, data = {}, params = {} } = finalRequestOptions;
51
57
  let requestDataOrParams = params;
52
58
  if (method2.toLowerCase() === "post") {
53
59
  requestDataOrParams = data;
@@ -60,18 +66,9 @@ function createBaseRequest(baseOptions) {
60
66
  const {
61
67
  enableCache = false,
62
68
  cacheData = false,
63
- defaultErrorCodeHandler = _defaultErrorCodeHandler.bind(
64
- null,
65
- defaultMessageShower
66
- ),
67
- defaultHttpErrorCodeHandler = _defaultHttpErrorCodeHandler.bind(
68
- null,
69
- defaultMessageShower
70
- ),
71
- otherErrorHandler = _defaultOtherErrorCodeHandler.bind(
72
- null,
73
- defaultMessageShower
74
- ),
69
+ defaultErrorCodeHandler = _defaultErrorCodeHandler.bind(null, defaultMessageShower),
70
+ defaultHttpErrorCodeHandler = _defaultHttpErrorCodeHandler.bind(null, defaultMessageShower),
71
+ otherErrorHandler = _defaultOtherErrorCodeHandler.bind(null, defaultMessageShower),
75
72
  errorCodePath = "code",
76
73
  cacheTime: cacheTime2 = 60,
77
74
  errorCodeMap = {},
@@ -96,8 +93,8 @@ function createBaseRequest(baseOptions) {
96
93
  return instance.request({
97
94
  method: method2,
98
95
  url: url2,
99
- data: data ?? (requestParamsOrDataTransfer ? requestParamsOrDataTransfer(data) : data),
100
- params: params ?? (requestParamsOrDataTransfer ? requestParamsOrDataTransfer(params) : params),
96
+ data: requestParamsOrDataTransfer ? requestParamsOrDataTransfer(data) : data,
97
+ params: requestParamsOrDataTransfer ? requestParamsOrDataTransfer(params) : params,
101
98
  ...finalAxiosOptions
102
99
  }).then(
103
100
  async (res) => {
@@ -116,10 +113,7 @@ function createBaseRequest(baseOptions) {
116
113
  const retryTask = retry();
117
114
  if (retryTask) return retryTask;
118
115
  } else {
119
- const {
120
- replaceResData = res.data,
121
- throwError: handlerThrowError = "default"
122
- } = Object(
116
+ const { replaceResData = res.data, throwError: handlerThrowError = "default" } = Object(
123
117
  await customHandler(errorCode, res.data, res, {
124
118
  ...requestOptions,
125
119
  ...requestParam
@@ -143,19 +137,14 @@ function createBaseRequest(baseOptions) {
143
137
  async (error) => {
144
138
  if (error.response) {
145
139
  let resData = error;
146
- const {
147
- [error.response.status]: customHandler = defaultHttpErrorCodeHandler
148
- } = httpErrorCodeMap;
140
+ const { [error.response.status]: customHandler = defaultHttpErrorCodeHandler } = httpErrorCodeMap;
149
141
  const err = new RequestError("服务端错误", "http", error);
150
142
  if (typeof customHandler === "string") {
151
143
  defaultMessageShower(customHandler);
152
144
  const retryTask = retry();
153
145
  if (retryTask) return retryTask;
154
146
  } else {
155
- const {
156
- replaceResData = error,
157
- throwError: handlerThrowError = "default"
158
- } = Object(
147
+ const { replaceResData = error, throwError: handlerThrowError = "default" } = Object(
159
148
  await customHandler(error.response.status, error, {
160
149
  ...requestOptions,
161
150
  ...requestParam
@@ -180,12 +169,7 @@ function createBaseRequest(baseOptions) {
180
169
  const err = new RequestError("服务端错误", "http", error);
181
170
  err.type = "http";
182
171
  err.data = error;
183
- const {
184
- replaceResData = error,
185
- throwError: handlerThrowError = "default"
186
- } = Object(
187
- await otherErrorHandler(error)
188
- );
172
+ const { replaceResData = error, throwError: handlerThrowError = "default" } = Object(await otherErrorHandler(error));
189
173
  const retryTask = retry();
190
174
  if (retryTask) return retryTask;
191
175
  resData = replaceResData;
@@ -1,4 +1,4 @@
1
1
  export { createStoreGetter, createStoreGetterMemo } from './createGetter';
2
2
  export { default as createStateStore } from './createStateStore';
3
3
  export type { StoreGetter, GetterNameMap, ReducedData } from './createGetter';
4
- export type { IHookStateInitialSetter, IHookStateInitAction, IHookStateSetter, IHookStateSetAction, IHookStateResolvable } from './createStateStore';
4
+ export type { IHookStateInitialSetter, IHookStateInitAction, IHookStateSetter, IHookStateSetAction, IHookStateResolvable, } from './createStateStore';
@@ -7,38 +7,30 @@ function VRequired(noneVals = [void 0]) {
7
7
  return true;
8
8
  });
9
9
  }
10
- const VString = BaseValidator.decoratorCreator(
11
- (val) => {
12
- if (typeof val !== "string") {
13
- return false;
14
- }
15
- return true;
10
+ const VString = BaseValidator.decoratorCreator((val) => {
11
+ if (typeof val !== "string") {
12
+ return false;
16
13
  }
17
- );
18
- const VNumber = BaseValidator.decoratorCreator(
19
- (val) => {
20
- if (typeof val !== "number") {
21
- return false;
22
- }
23
- return true;
14
+ return true;
15
+ });
16
+ const VNumber = BaseValidator.decoratorCreator((val) => {
17
+ if (typeof val !== "number") {
18
+ return false;
24
19
  }
25
- );
26
- const VArray = BaseValidator.decoratorCreator(
27
- (val) => {
28
- if (!Array.isArray(val)) {
29
- return false;
30
- }
31
- return true;
20
+ return true;
21
+ });
22
+ const VArray = BaseValidator.decoratorCreator((val) => {
23
+ if (!Array.isArray(val)) {
24
+ return false;
32
25
  }
33
- );
34
- const VBoolean = BaseValidator.decoratorCreator(
35
- (val) => {
36
- if (typeof val !== "boolean") {
37
- return false;
38
- }
39
- return true;
26
+ return true;
27
+ });
28
+ const VBoolean = BaseValidator.decoratorCreator((val) => {
29
+ if (typeof val !== "boolean") {
30
+ return false;
40
31
  }
41
- );
32
+ return true;
33
+ });
42
34
  const VMin = (min) => BaseValidator.decoratorCreator((val) => {
43
35
  if (typeof val !== "number" || val < min) {
44
36
  return false;
@@ -69,15 +61,13 @@ const VMaxLength = (maxLen) => BaseValidator.decoratorCreator((val) => {
69
61
  }
70
62
  return true;
71
63
  });
72
- const VEmail = BaseValidator.decoratorCreator(
73
- (val) => {
74
- if (typeof val !== "string") {
75
- return false;
76
- }
77
- const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
78
- return emailReg.test(val);
64
+ const VEmail = BaseValidator.decoratorCreator((val) => {
65
+ if (typeof val !== "string") {
66
+ return false;
79
67
  }
80
- );
68
+ const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
69
+ return emailReg.test(val);
70
+ });
81
71
  const VPattern = (pattern) => BaseValidator.decoratorCreator((val) => {
82
72
  if (typeof val !== "string") {
83
73
  return false;
@@ -1,2 +1,2 @@
1
1
  export { BaseValidator } from './validator';
2
- export { VRequired, VString, VNumber, VEmail, VMinLength, VArray, VBoolean, VPattern, VMaxLength, VMax, VMin } from './decorators';
2
+ export { VRequired, VString, VNumber, VEmail, VMinLength, VArray, VBoolean, VPattern, VMaxLength, VMax, VMin, } from './decorators';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rxtutils",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "type": "module",
5
5
  "main": "cjs/index.cjs",
6
6
  "module": "es/index.mjs",
@@ -51,7 +51,14 @@
51
51
  "scripts": {
52
52
  "dev": "vite serve playground --config playground/vite.config.ts",
53
53
  "build": "pnpm clean && vite build",
54
- "clean": "rimraf es cjs",
54
+ "clean": "rimraf es cjs dist",
55
+ "format": "prettier . --write",
56
+ "format:check": "prettier . --check",
57
+ "lint": "eslint . --max-warnings=0",
58
+ "lint:fix": "eslint . --fix",
59
+ "test": "vitest run",
60
+ "test:watch": "vitest",
61
+ "typecheck": "tsc -p tsconfig.test.json --noEmit",
55
62
  "publishNpm": "npm publish --registry https://registry.npmjs.org",
56
63
  "loginNpm": "npm login --registry https://registry.npmjs.org"
57
64
  },
@@ -59,28 +66,42 @@
59
66
  "@babel/plugin-proposal-class-properties": "^7.18.6",
60
67
  "@babel/plugin-proposal-decorators": "^7.28.6",
61
68
  "@babel/plugin-transform-class-static-block": "^7.28.6",
69
+ "@eslint/js": "^9.39.2",
62
70
  "@rollup/plugin-typescript": "^12.3.0",
71
+ "@testing-library/react": "^16.3.2",
63
72
  "@types/lodash-es": "^4.17.12",
64
73
  "@types/node": "^22.19.5",
65
74
  "@types/react": "^19.2.8",
66
75
  "@types/react-dom": "^19.2.3",
67
76
  "@vitejs/plugin-react": "^5.1.2",
68
77
  "babel-plugin-react-compiler": "^1.0.0",
69
- "react-dom": "^19.2.3",
78
+ "eslint": "^9.39.2",
79
+ "eslint-config-prettier": "^10.1.8",
80
+ "eslint-plugin-prettier": "^5.5.5",
81
+ "eslint-plugin-react-hooks": "^7.0.1",
82
+ "eslint-plugin-vitest": "^0.5.4",
83
+ "fake-indexeddb": "^6.2.2",
84
+ "globals": "^17.3.0",
85
+ "jsdom": "^28.0.0",
86
+ "prettier": "^3.8.1",
87
+ "react": "^19.2.4",
88
+ "react-dom": "^19.2.4",
70
89
  "rimraf": "^6.1.2",
71
90
  "rollup": "^4.55.1",
72
91
  "rollup-plugin-dts": "^6.3.0",
73
92
  "typescript": "^5.9.3",
93
+ "typescript-eslint": "^8.54.0",
74
94
  "vite": "^7.3.1",
75
- "vite-plugin-dts": "^4.5.4"
95
+ "vite-plugin-dts": "^4.5.4",
96
+ "vitest": "^4.0.18"
76
97
  },
77
98
  "peerDependencies": {
78
99
  "react": "^19.2.3"
79
100
  },
80
101
  "dependencies": {
81
102
  "axios": "^1.13.2",
103
+ "date-fns": "^4.1.0",
82
104
  "lodash-es": "^4.17.22",
83
- "moment": "^2.30.1",
84
105
  "tslib": "^2.8.1"
85
106
  }
86
- }
107
+ }