@net-vert/core 0.0.9 → 0.1.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/README.md +253 -28
- package/dist/index.d.ts +17 -13
- package/dist/index.js +135 -188
- package/dist/index.umd.cjs +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -83,7 +83,6 @@ retryApi.get('/unstable-api');
|
|
|
83
83
|
## 🔎 缓存设计说明
|
|
84
84
|
|
|
85
85
|
- 本库默认内置缓存支持
|
|
86
|
-
- **后续将完全依赖官方独立包:`@net-vert/cache`**
|
|
87
86
|
- 用户无需关注存储机制,专注请求本身
|
|
88
87
|
|
|
89
88
|
---
|
|
@@ -98,64 +97,117 @@ retryApi.get('/unstable-api');
|
|
|
98
97
|
| `requestExtender.idempotencyRequestor(options)` | 创建幂等请求器 |
|
|
99
98
|
| `requestExtender.retryRequestor(options)` | 创建重试请求器 |
|
|
100
99
|
|
|
101
|
-
非常专业!以下是针对你这份缓存扩展的专属文档(带源码思路和示例):
|
|
102
100
|
|
|
103
101
|
---
|
|
104
102
|
|
|
105
103
|
## 🗂 缓存扩展 `requestExtender.cacheRequestor(options)`
|
|
106
104
|
|
|
107
|
-
`cacheRequestor` 是 `@net-vert/core`
|
|
105
|
+
`cacheRequestor` 是 `@net-vert/core` 内置的智能缓存增强器,提供多维度缓存控制能力,支持同步/异步校验策略,所有cacheRequestor共享一个存储空间。
|
|
108
106
|
|
|
109
107
|
### ✅ 核心特性
|
|
110
108
|
|
|
111
|
-
-
|
|
112
|
-
-
|
|
113
|
-
|
|
114
|
-
-
|
|
115
|
-
|
|
109
|
+
- **🚀 多级缓存策略**
|
|
110
|
+
内存缓存 + 持久化存储(未来由 `@net-vert/cache` 提供)
|
|
111
|
+
|
|
112
|
+
- **🔗 智能并发合并**
|
|
113
|
+
相同请求共享 Promise,避免重复网络消耗
|
|
114
|
+
|
|
115
|
+
- **⏳ 动态缓存控制**
|
|
116
|
+
支持时间/逻辑双重失效校验机制
|
|
117
|
+
|
|
118
|
+
- **🧩 弹性校验策略**
|
|
119
|
+
支持同步/异步缓存有效性检查
|
|
116
120
|
|
|
117
121
|
---
|
|
118
122
|
|
|
119
|
-
### ⚙️
|
|
123
|
+
### ⚙️ 配置参数
|
|
120
124
|
|
|
121
|
-
| 参数 | 类型
|
|
122
|
-
|
|
123
|
-
| `key` | `(config: UnifiedConfig) => string`
|
|
124
|
-
| `persist` | `boolean`
|
|
125
|
-
| `
|
|
125
|
+
| 参数 | 类型 | 说明 | 默认值 |
|
|
126
|
+
|--------------|--------------------------------------------------------------------|-------------------------------------------------------------|----------------|
|
|
127
|
+
| `key` | `(config: UnifiedConfig) => string` | 自定义缓存键生成规则 | `config.url` |
|
|
128
|
+
| `persist` | `boolean` | 启用持久化存储(默认内存存储) | `false` |
|
|
129
|
+
| `duration` | `number` 或 `({ key, config, response }) => number` | 缓存时间(ms)或动态计算函数 | `Infinity` |
|
|
130
|
+
| `isValid` | `({ key, config, cachedData }) => boolean \| Promise<boolean>` | 缓存有效性校验(支持异步校验) | - |
|
|
126
131
|
|
|
127
132
|
---
|
|
128
133
|
|
|
129
|
-
### 📥
|
|
134
|
+
### 📥 基础使用
|
|
130
135
|
|
|
131
136
|
```typescript
|
|
132
137
|
import { requestExtender } from '@net-vert/core';
|
|
133
138
|
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
key:
|
|
139
|
+
const api = requestExtender.cacheRequestor({
|
|
140
|
+
duration: 3000, // 缓存3秒
|
|
141
|
+
key: config => `${config.method}:${config.url}` // 复合键
|
|
137
142
|
});
|
|
138
143
|
|
|
139
|
-
//
|
|
140
|
-
|
|
144
|
+
// 首次请求将缓存
|
|
145
|
+
api.get('/user').then(console.log);
|
|
141
146
|
|
|
142
|
-
//
|
|
143
|
-
|
|
147
|
+
// 3秒内相同请求直接返回缓存
|
|
148
|
+
api.get('/user').then(console.log);
|
|
144
149
|
```
|
|
145
150
|
|
|
146
|
-
|
|
151
|
+
### 🎯 动态缓存示例
|
|
147
152
|
|
|
148
|
-
|
|
153
|
+
#### 根据响应数据设置缓存时间
|
|
154
|
+
```typescript
|
|
155
|
+
requestExtender.cacheRequestor({
|
|
156
|
+
duration: ({ response }) => response.data.isHot ? 10000 : 3000
|
|
157
|
+
});
|
|
158
|
+
```
|
|
149
159
|
|
|
160
|
+
#### 权限变更时失效缓存
|
|
150
161
|
```typescript
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return response.isHot ? 10000 : 3000;
|
|
162
|
+
requestExtender.cacheRequestor({
|
|
163
|
+
isValid: ({ cachedData }) => {
|
|
164
|
+
return cachedData.value.permission === currentUser.permission
|
|
155
165
|
}
|
|
156
166
|
});
|
|
157
167
|
```
|
|
158
168
|
|
|
169
|
+
#### 异步校验缓存有效性
|
|
170
|
+
```typescript
|
|
171
|
+
requestExtender.cacheRequestor({
|
|
172
|
+
async isValid({ key }) {
|
|
173
|
+
const { valid } = await fetch('/cache/validate', { body: key })
|
|
174
|
+
return valid
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
### 🛠 工作机制
|
|
182
|
+
|
|
183
|
+
```mermaid
|
|
184
|
+
graph TB
|
|
185
|
+
A[请求进入] --> B{存在Promise缓存?}
|
|
186
|
+
B -->|是| C[共享请求]
|
|
187
|
+
B -->|否| D{存在物理缓存?}
|
|
188
|
+
D -->|是| E[执行isValid校验]
|
|
189
|
+
E -->|有效| F[返回缓存]
|
|
190
|
+
E -->|无效| G[清理缓存]
|
|
191
|
+
D -->|否| H[发起新请求]
|
|
192
|
+
H --> I[缓存响应数据]
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
### ⚠️ 注意事项
|
|
198
|
+
|
|
199
|
+
1. **缓存穿透防护**
|
|
200
|
+
当 `isValid` 返回 `false` 时会主动清理缓存,后续请求将触发新请求
|
|
201
|
+
|
|
202
|
+
2. **异步校验建议**
|
|
203
|
+
耗时较长的异步校验建议配合 `duration` 使用,避免校验期间重复请求
|
|
204
|
+
|
|
205
|
+
3. **内存管理**
|
|
206
|
+
高频数据建议启用 `persist` 持久化存储,防止内存溢出
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
|
|
159
211
|
## ♻️ 幂等扩展 `requestExtender.idempotencyRequestor(options)`
|
|
160
212
|
|
|
161
213
|
`idempotencyRequestor` 是基于 `cacheRequestor` 封装的幂等增强器,确保同一参数的请求,在请求未完成前只发送一次,自动合并并发请求,避免重复提交和资源浪费。
|
|
@@ -298,6 +350,179 @@ idempotentApi.post('/user/create', { name: 'Alice' }).then(console.log);
|
|
|
298
350
|
|
|
299
351
|
---
|
|
300
352
|
|
|
353
|
+
```markdown
|
|
354
|
+
# 🚀 并发请求控制器 `createConcurrentPoolRequestor`
|
|
355
|
+
|
|
356
|
+
提供智能并发控制与自动重试能力的请求扩展器,适用于需要精准控制请求并发的场景。
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## 📦 核心模块
|
|
361
|
+
|
|
362
|
+
### 1. 并发池 `ConcurrentPool`
|
|
363
|
+
```typescript
|
|
364
|
+
export class ConcurrentPool {
|
|
365
|
+
parallelCount: number // 最大并行任务数
|
|
366
|
+
tasks: TaskItemList // 待执行任务队列
|
|
367
|
+
runningCount: number // 当前运行中任务数
|
|
368
|
+
|
|
369
|
+
constructor(parallelCount = 4) // 初始化并发池
|
|
370
|
+
|
|
371
|
+
// 添加任务到队列
|
|
372
|
+
add(id: string, task: Task): Promise<any>
|
|
373
|
+
|
|
374
|
+
// 移除指定任务
|
|
375
|
+
remove(id: string): void
|
|
376
|
+
|
|
377
|
+
// 执行单个任务(内部方法)
|
|
378
|
+
private execute(currentTask: TaskItem): void
|
|
379
|
+
|
|
380
|
+
// 启动任务处理(内部调度器)
|
|
381
|
+
private _run(): void
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
### 2. 请求器工厂函数
|
|
388
|
+
```typescript
|
|
389
|
+
createConcurrentPoolRequestor(config): {
|
|
390
|
+
requestor: Requestor, // 增强后的请求器实例
|
|
391
|
+
concurrentPool: ConcurrentPool // 关联的并发池
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## ⚙️ 配置参数
|
|
398
|
+
|
|
399
|
+
| 参数 | 类型 | 说明 | 默认值 |
|
|
400
|
+
|------------------|---------------------------------------|------------------------------|---------------------|
|
|
401
|
+
| `parallelCount` | `number` | 最大并行请求数 | 4 |
|
|
402
|
+
| `createId` | `(config: UnifiedConfig) => string` | 生成唯一任务ID的函数 | 时间戳+随机数 |
|
|
403
|
+
| `retries` | `number` | 失败重试次数 | 0 (不重试) |
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## 🎯 功能特性
|
|
408
|
+
|
|
409
|
+
### 1. 智能并发控制
|
|
410
|
+
```mermaid
|
|
411
|
+
graph TD
|
|
412
|
+
A[新请求到达] --> B{运行中任务 < 最大并发数?}
|
|
413
|
+
B -->|是| C[立即执行]
|
|
414
|
+
B -->|否| D[进入等待队列]
|
|
415
|
+
C --> E[任务完成]
|
|
416
|
+
E --> F{队列有等待任务?}
|
|
417
|
+
F -->|是| G[触发下一个任务]
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### 2. 自动重试机制
|
|
421
|
+
```typescript
|
|
422
|
+
// 集成重试模块的工作流
|
|
423
|
+
sequenceDiagram
|
|
424
|
+
participant P as 并发池
|
|
425
|
+
participant R as 重试模块
|
|
426
|
+
participant S as 服务器
|
|
427
|
+
|
|
428
|
+
P->>R: 执行请求
|
|
429
|
+
R->>S: 尝试请求
|
|
430
|
+
alt 成功
|
|
431
|
+
S-->>R: 返回数据
|
|
432
|
+
R-->>P: 传递结果
|
|
433
|
+
else 失败
|
|
434
|
+
R->>R: 重试逻辑(最多retries次)
|
|
435
|
+
R-->>P: 最终结果/错误
|
|
436
|
+
end
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## 📝 使用示例
|
|
442
|
+
|
|
443
|
+
### 基础使用
|
|
444
|
+
```typescript
|
|
445
|
+
import createConcurrentPoolRequestor from '@/requests/modules/concurrentPoolRequestor'
|
|
446
|
+
|
|
447
|
+
// 创建最大并发数为3的请求器
|
|
448
|
+
const { requestor } = createConcurrentPoolRequestor({
|
|
449
|
+
parallelCount: 3,
|
|
450
|
+
retries: 2, // 失败自动重试2次
|
|
451
|
+
delay: 500 // 重试间隔500ms
|
|
452
|
+
})
|
|
453
|
+
|
|
454
|
+
// 批量发起请求
|
|
455
|
+
const requests = Array(10).fill(0).map(() =>
|
|
456
|
+
requestor.get('/api/data')
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
Promise.all(requests).then(results => {
|
|
460
|
+
console.log('所有请求完成:', results)
|
|
461
|
+
})
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### 高级控制
|
|
465
|
+
```typescript
|
|
466
|
+
// 获取并发池实例进行精细控制
|
|
467
|
+
const { requestor, concurrentPool } = createConcurrentPoolRequestor()
|
|
468
|
+
|
|
469
|
+
// 动态调整并发数
|
|
470
|
+
concurrentPool.parallelCount = 5
|
|
471
|
+
|
|
472
|
+
// 取消特定请求
|
|
473
|
+
const reqId = 'custom-id-123'
|
|
474
|
+
requestor.post('/api/submit', { data }, {
|
|
475
|
+
__id: reqId // 通过配置注入自定义ID
|
|
476
|
+
}).catch(err => {
|
|
477
|
+
if (err.message === 'ABORTED') {
|
|
478
|
+
console.log('请求被主动取消')
|
|
479
|
+
}
|
|
480
|
+
})
|
|
481
|
+
|
|
482
|
+
// 主动取消任务
|
|
483
|
+
concurrentPool.remove(reqId)
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
488
|
+
## ⚠️ 注意事项
|
|
489
|
+
|
|
490
|
+
1. **ID生成策略**
|
|
491
|
+
确保`createId`函数生成的ID具有唯一性:
|
|
492
|
+
```typescript
|
|
493
|
+
createId: config => `${config.method}:${config.url}:${hash(config.params)}`
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
2. **资源释放**
|
|
497
|
+
长时间运行的实例需手动释放资源:
|
|
498
|
+
```typescript
|
|
499
|
+
// 清空任务队列
|
|
500
|
+
concurrentPool.tasks.clear()
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
---
|
|
504
|
+
|
|
505
|
+
## 🛠 设计理念
|
|
506
|
+
|
|
507
|
+
### 1. 队列优先级策略
|
|
508
|
+
```typescript
|
|
509
|
+
// 可扩展为优先级队列
|
|
510
|
+
interface PriorityTaskItem extends TaskItem {
|
|
511
|
+
priority: number
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// 自定义队列实现
|
|
515
|
+
class PriorityQueue implements TaskItemList {
|
|
516
|
+
enqueue(id: string, item: PriorityTaskItem) {
|
|
517
|
+
// 根据优先级插入队列
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
### 1. 队列优先级策略
|
|
521
|
+
```typescript
|
|
522
|
+
一个实例对象控制着单独的并发池
|
|
523
|
+
|
|
524
|
+
```
|
|
525
|
+
|
|
301
526
|
## 📤 开源信息
|
|
302
527
|
|
|
303
528
|
- 仓库地址:[https://github.com/yvygyyth/net-vert](https://github.com/yvygyyth/net-vert)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { TaskQueue } from 'id-queue';
|
|
2
2
|
|
|
3
|
-
declare interface BasicCredentials {
|
|
4
|
-
username: string;
|
|
5
|
-
password: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
3
|
declare class ConcurrentPool {
|
|
9
4
|
parallelCount: number;
|
|
10
5
|
tasks: TaskItemList;
|
|
@@ -26,8 +21,6 @@ declare interface RequestConfig<D = any> {
|
|
|
26
21
|
params?: Record<string, any> | string;
|
|
27
22
|
data?: D;
|
|
28
23
|
timeout?: number;
|
|
29
|
-
auth?: BasicCredentials;
|
|
30
|
-
responseType?: ResponseType;
|
|
31
24
|
onUploadProgress?: <P extends ProgressEvent>(progressEvent: P) => void;
|
|
32
25
|
onDownloadProgress?: <P extends ProgressEvent>(progressEvent: P) => void;
|
|
33
26
|
validateStatus?: ((status: number) => boolean) | null;
|
|
@@ -38,14 +31,23 @@ export declare const requestExtender: {
|
|
|
38
31
|
cacheRequestor: (config?: {
|
|
39
32
|
key?: (config: UnifiedConfig) => string;
|
|
40
33
|
persist?: boolean;
|
|
41
|
-
|
|
34
|
+
duration?: number | (({ key, config, response }: {
|
|
35
|
+
key: string;
|
|
42
36
|
config: UnifiedConfig;
|
|
43
37
|
response: any;
|
|
44
38
|
}) => number);
|
|
39
|
+
isValid?: (params: {
|
|
40
|
+
key: string;
|
|
41
|
+
config: UnifiedConfig;
|
|
42
|
+
cachedData: {
|
|
43
|
+
value: any;
|
|
44
|
+
expiresAt: number;
|
|
45
|
+
};
|
|
46
|
+
}) => boolean | Promise<boolean>;
|
|
45
47
|
}) => Requestor;
|
|
46
48
|
idempotencyRequestor: (genKey?: (config: UnifiedConfig) => string) => Requestor;
|
|
47
49
|
retryRequestor: (config?: RetryOptions) => Requestor;
|
|
48
|
-
concurrentPoolRequestor: (config
|
|
50
|
+
concurrentPoolRequestor: (config?: {
|
|
49
51
|
parallelCount?: number;
|
|
50
52
|
createId?: (config: UnifiedConfig) => string;
|
|
51
53
|
} & RetryOptions) => {
|
|
@@ -55,11 +57,11 @@ export declare const requestExtender: {
|
|
|
55
57
|
};
|
|
56
58
|
|
|
57
59
|
export declare interface Requestor {
|
|
58
|
-
get<R = any, D = any>(url: string, config?: RequestConfig<D
|
|
60
|
+
get<R = any, D = any>(url: string, config?: WithDynamicProps<RequestConfig<D>>): Promise<R>;
|
|
59
61
|
post<R = any, D = any>(url: string, data?: D, config?: RequestConfig<D>): Promise<R>;
|
|
60
|
-
delete<R = any, D = any>(url: string, config?: RequestConfig<D
|
|
61
|
-
put<R = any, D = any>(url: string, data?: D, config?: RequestConfig<D
|
|
62
|
-
request<R = any, D = any>(config: UnifiedConfig<D
|
|
62
|
+
delete<R = any, D = any>(url: string, config?: WithDynamicProps<RequestConfig<D>>): Promise<R>;
|
|
63
|
+
put<R = any, D = any>(url: string, data?: D, config?: WithDynamicProps<RequestConfig<D>>): Promise<R>;
|
|
64
|
+
request<R = any, D = any>(config: WithDynamicProps<UnifiedConfig<D>>): Promise<R>;
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
declare type RetryOptions = {
|
|
@@ -87,4 +89,6 @@ export declare type UnifiedRequestor = <R = any, D = any>(config: UnifiedConfig<
|
|
|
87
89
|
|
|
88
90
|
export declare const useRequestor: (instanceKey?: string) => Requestor;
|
|
89
91
|
|
|
92
|
+
declare type WithDynamicProps<T, V = any> = T & Record<string, V>;
|
|
93
|
+
|
|
90
94
|
export { }
|
package/dist/index.js
CHANGED
|
@@ -1,232 +1,179 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import h from "localforage";
|
|
2
|
+
import { TaskQueue as I } from "id-queue";
|
|
3
|
+
const f = {
|
|
4
|
+
get: (t, e) => ({
|
|
5
|
+
url: t,
|
|
4
6
|
method: "get",
|
|
5
7
|
...e,
|
|
6
8
|
params: e == null ? void 0 : e.params
|
|
7
9
|
}),
|
|
8
|
-
post: (
|
|
9
|
-
url:
|
|
10
|
+
post: (t, e, r) => ({
|
|
11
|
+
url: t,
|
|
10
12
|
method: "post",
|
|
11
13
|
data: e,
|
|
12
|
-
headers: { "Content-Type": "application/json", ...
|
|
13
|
-
...
|
|
14
|
+
headers: { "Content-Type": "application/json", ...r == null ? void 0 : r.headers },
|
|
15
|
+
...r
|
|
14
16
|
}),
|
|
15
|
-
delete: (
|
|
16
|
-
url:
|
|
17
|
+
delete: (t, e) => ({
|
|
18
|
+
url: t,
|
|
17
19
|
method: "delete",
|
|
18
20
|
...e
|
|
19
21
|
}),
|
|
20
|
-
put: (
|
|
21
|
-
url:
|
|
22
|
+
put: (t, e, r) => ({
|
|
23
|
+
url: t,
|
|
22
24
|
method: "put",
|
|
23
25
|
data: e,
|
|
24
|
-
headers: { "Content-Type": "application/json", ...
|
|
25
|
-
...
|
|
26
|
+
headers: { "Content-Type": "application/json", ...r == null ? void 0 : r.headers },
|
|
27
|
+
...r
|
|
26
28
|
}),
|
|
27
|
-
request: (
|
|
29
|
+
request: (t) => t
|
|
28
30
|
};
|
|
29
|
-
function
|
|
31
|
+
function j(t) {
|
|
30
32
|
const e = {};
|
|
31
|
-
return Object.keys(
|
|
32
|
-
(
|
|
33
|
-
e[
|
|
34
|
-
const n =
|
|
35
|
-
return
|
|
33
|
+
return Object.keys(f).forEach(
|
|
34
|
+
(r) => {
|
|
35
|
+
e[r] = (...o) => {
|
|
36
|
+
const n = f[r](...o);
|
|
37
|
+
return t(n);
|
|
36
38
|
};
|
|
37
39
|
}
|
|
38
40
|
), {
|
|
39
41
|
...e,
|
|
40
|
-
request:
|
|
42
|
+
request: t
|
|
41
43
|
};
|
|
42
44
|
}
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
},
|
|
46
|
-
const e =
|
|
47
|
-
if (!e) throw new Error(`Requestor实例 ${
|
|
45
|
+
const R = "default", k = /* @__PURE__ */ new Map(), U = (t, e = R) => {
|
|
46
|
+
k.set(e, j(t));
|
|
47
|
+
}, g = (t = R) => {
|
|
48
|
+
const e = k.get(t);
|
|
49
|
+
if (!e) throw new Error(`Requestor实例 ${t} 未注册`);
|
|
48
50
|
return e;
|
|
49
51
|
};
|
|
50
|
-
class
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return !!localStorage.getItem(e);
|
|
52
|
+
class E {
|
|
53
|
+
constructor(e) {
|
|
54
|
+
e && h.config(e);
|
|
54
55
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const t = localStorage.getItem(e);
|
|
58
|
-
if (t)
|
|
59
|
-
try {
|
|
60
|
-
return JSON.parse(t);
|
|
61
|
-
} catch (s) {
|
|
62
|
-
console.error("Error parsing cached data", s);
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
56
|
+
async has(e) {
|
|
57
|
+
return await h.getItem(e) !== null;
|
|
65
58
|
}
|
|
66
|
-
|
|
67
|
-
set(e, t) {
|
|
59
|
+
async get(e) {
|
|
68
60
|
try {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
61
|
+
return await h.getItem(e) ?? void 0;
|
|
62
|
+
} catch (r) {
|
|
63
|
+
console.error("Error getting data", r);
|
|
64
|
+
return;
|
|
73
65
|
}
|
|
74
|
-
return t;
|
|
75
66
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
67
|
+
async set(e, r) {
|
|
68
|
+
return await h.setItem(e, r), r;
|
|
69
|
+
}
|
|
70
|
+
async delete(e) {
|
|
71
|
+
await h.removeItem(e);
|
|
79
72
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
localStorage.clear();
|
|
73
|
+
async clear() {
|
|
74
|
+
await h.clear();
|
|
83
75
|
}
|
|
84
76
|
}
|
|
85
|
-
const
|
|
86
|
-
const
|
|
77
|
+
const S = new E(), T = /* @__PURE__ */ new Map(), $ = (t) => t ? S : T, D = () => {
|
|
78
|
+
const t = /* @__PURE__ */ new Map();
|
|
87
79
|
return {
|
|
88
|
-
getPromise: (
|
|
89
|
-
setPromise: (
|
|
90
|
-
|
|
80
|
+
getPromise: (s) => t.get(s),
|
|
81
|
+
setPromise: (s, i) => {
|
|
82
|
+
t.set(s, i);
|
|
91
83
|
},
|
|
92
|
-
delPromise: (
|
|
93
|
-
|
|
84
|
+
delPromise: (s) => {
|
|
85
|
+
t.delete(s);
|
|
94
86
|
},
|
|
95
87
|
clearCache: () => {
|
|
96
|
-
|
|
88
|
+
t.clear();
|
|
97
89
|
}
|
|
98
90
|
};
|
|
99
|
-
},
|
|
100
|
-
|
|
101
|
-
persist: !1,
|
|
102
|
-
cacheTime: 1 / 0
|
|
103
|
-
}, x = (r, e) => ({
|
|
104
|
-
value: r,
|
|
91
|
+
}, _ = (t, e) => ({
|
|
92
|
+
value: t,
|
|
105
93
|
expiresAt: Date.now() + e
|
|
106
|
-
}), z = (
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
94
|
+
}), z = (t) => t.expiresAt > Date.now(), A = {
|
|
95
|
+
key: (t) => t.url,
|
|
96
|
+
persist: !1,
|
|
97
|
+
duration: 1 / 0
|
|
98
|
+
}, x = (t) => {
|
|
99
|
+
const e = { ...A, ...t }, r = $(e.persist), { getPromise: o, setPromise: n, delPromise: s } = D(), i = {
|
|
100
|
+
get(d, c) {
|
|
101
|
+
return async (...u) => {
|
|
102
|
+
const l = f[c](...u), a = e.key(l), p = o(a);
|
|
103
|
+
if (p)
|
|
104
|
+
return p;
|
|
105
|
+
const C = r.get(a);
|
|
106
|
+
let w = !1;
|
|
107
|
+
if (C && z(C)) {
|
|
108
|
+
try {
|
|
109
|
+
w = e.isValid ? await e.isValid({
|
|
110
|
+
key: a,
|
|
111
|
+
config: l,
|
|
112
|
+
cachedData: C
|
|
113
|
+
}) : !0;
|
|
114
|
+
} catch (m) {
|
|
115
|
+
console.error(`校验异常 ${a}`, m);
|
|
116
|
+
}
|
|
117
|
+
w || r.delete(a);
|
|
127
118
|
}
|
|
119
|
+
if (w)
|
|
120
|
+
return C.value;
|
|
121
|
+
const P = Reflect.apply(d[c], d, u).then(async (m) => {
|
|
122
|
+
const v = typeof e.duration == "number" ? e.duration : e.duration({
|
|
123
|
+
key: a,
|
|
124
|
+
config: l,
|
|
125
|
+
response: m
|
|
126
|
+
});
|
|
127
|
+
return r.set(a, _(m, v)), m;
|
|
128
|
+
}).finally(() => {
|
|
129
|
+
s(a);
|
|
130
|
+
});
|
|
131
|
+
return n(a, P), P;
|
|
128
132
|
};
|
|
129
133
|
}
|
|
130
134
|
};
|
|
131
|
-
return new Proxy(
|
|
132
|
-
}, O = (
|
|
133
|
-
const { method: e, url:
|
|
134
|
-
return [e,
|
|
135
|
-
},
|
|
136
|
-
key: (e) =>
|
|
135
|
+
return new Proxy(g(), i);
|
|
136
|
+
}, O = (t) => {
|
|
137
|
+
const { method: e, url: r, params: o, data: n } = t;
|
|
138
|
+
return [e, r, JSON.stringify(o), JSON.stringify(n)].join("|");
|
|
139
|
+
}, H = (t) => x({
|
|
140
|
+
key: (e) => t ? t(e) : O(e),
|
|
137
141
|
persist: !1
|
|
138
|
-
}),
|
|
142
|
+
}), V = {
|
|
139
143
|
retries: 3,
|
|
140
144
|
delay: 0,
|
|
141
145
|
retryCondition: () => !0
|
|
142
|
-
},
|
|
143
|
-
const { retries: e, delay:
|
|
144
|
-
get(
|
|
145
|
-
return (...
|
|
146
|
-
let
|
|
147
|
-
const
|
|
148
|
-
if (
|
|
149
|
-
|
|
150
|
-
const
|
|
146
|
+
}, M = (t) => {
|
|
147
|
+
const { retries: e, delay: r, retryCondition: o } = { ...V, ...t }, n = {
|
|
148
|
+
get(s, i) {
|
|
149
|
+
return (...d) => {
|
|
150
|
+
let c = 0;
|
|
151
|
+
const y = () => Reflect.apply(s[i], s, d).catch((u) => {
|
|
152
|
+
if (c < e && o(u)) {
|
|
153
|
+
c++;
|
|
154
|
+
const l = typeof r == "function" ? r(c) : r;
|
|
151
155
|
return new Promise((a) => {
|
|
152
|
-
setTimeout(() => a(
|
|
156
|
+
setTimeout(() => a(y()), l);
|
|
153
157
|
});
|
|
154
158
|
}
|
|
155
159
|
return Promise.reject(u);
|
|
156
160
|
});
|
|
157
|
-
return
|
|
161
|
+
return y();
|
|
158
162
|
};
|
|
159
163
|
}
|
|
160
164
|
};
|
|
161
|
-
return new Proxy(
|
|
165
|
+
return new Proxy(g(), n);
|
|
162
166
|
};
|
|
163
|
-
|
|
164
|
-
class N {
|
|
165
|
-
// 尾部指针(最新加入的任务)
|
|
166
|
-
constructor(e) {
|
|
167
|
-
if (q(this, "map"), q(this, "head"), q(this, "tail"), this.map = /* @__PURE__ */ new Map(), this.head = null, this.tail = null, e)
|
|
168
|
-
for (const [t, s] of e)
|
|
169
|
-
this.enqueue(t, s);
|
|
170
|
-
}
|
|
171
|
-
// 添加任务(O(1))
|
|
172
|
-
enqueue(e, t) {
|
|
173
|
-
this.has(e) && this.remove(e);
|
|
174
|
-
const s = { id: e, data: t, next: null, prev: this.tail };
|
|
175
|
-
this.tail ? this.tail.next = s : this.head = s, this.tail = s, this.map.set(e, s);
|
|
176
|
-
}
|
|
177
|
-
// 取出最早的任务(O(1))
|
|
178
|
-
dequeue() {
|
|
179
|
-
if (!this.head) return null;
|
|
180
|
-
const e = this.head;
|
|
181
|
-
return this.head = e.next, this.head ? this.head.prev = null : this.tail = null, this.map.delete(e.id), e.data;
|
|
182
|
-
}
|
|
183
|
-
// 通过 ID 删除任务(O(1))
|
|
184
|
-
remove(e) {
|
|
185
|
-
const t = this.map.get(e);
|
|
186
|
-
return t ? (t.prev ? t.prev.next = t.next : this.head = t.next, t.next ? t.next.prev = t.prev : this.tail = t.prev, this.map.delete(e), !0) : !1;
|
|
187
|
-
}
|
|
188
|
-
// 通过 ID 获取任务(O(1))
|
|
189
|
-
getTask(e) {
|
|
190
|
-
var t;
|
|
191
|
-
return (t = this.map.get(e)) == null ? void 0 : t.data;
|
|
192
|
-
}
|
|
193
|
-
// 通过id更新任务
|
|
194
|
-
update(e, t) {
|
|
195
|
-
const s = this.map.get(e);
|
|
196
|
-
return s ? (s.data = t, !0) : !1;
|
|
197
|
-
}
|
|
198
|
-
has(e) {
|
|
199
|
-
return this.map.has(e);
|
|
200
|
-
}
|
|
201
|
-
peek() {
|
|
202
|
-
var e;
|
|
203
|
-
return ((e = this.head) == null ? void 0 : e.data) ?? null;
|
|
204
|
-
}
|
|
205
|
-
clear() {
|
|
206
|
-
this.map.clear(), this.head = null, this.tail = null;
|
|
207
|
-
}
|
|
208
|
-
// 获取当前队列大小
|
|
209
|
-
get size() {
|
|
210
|
-
return this.map.size;
|
|
211
|
-
}
|
|
212
|
-
get queue() {
|
|
213
|
-
const e = [];
|
|
214
|
-
let t = this.head;
|
|
215
|
-
for (; t; )
|
|
216
|
-
e.push(t.data), t = t.next;
|
|
217
|
-
return e;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
class $ {
|
|
167
|
+
class b {
|
|
221
168
|
constructor(e = 4) {
|
|
222
|
-
this.parallelCount = e, this.tasks = new
|
|
169
|
+
this.parallelCount = e, this.tasks = new I(), this.runningCount = 0;
|
|
223
170
|
}
|
|
224
171
|
// 加入
|
|
225
|
-
add(e,
|
|
226
|
-
return console.log("poolinset", e,
|
|
172
|
+
add(e, r) {
|
|
173
|
+
return console.log("poolinset", e, r), new Promise((o, n) => {
|
|
227
174
|
this.tasks.enqueue(e, {
|
|
228
|
-
task:
|
|
229
|
-
resolve:
|
|
175
|
+
task: r,
|
|
176
|
+
resolve: o,
|
|
230
177
|
reject: n
|
|
231
178
|
}), this._run();
|
|
232
179
|
});
|
|
@@ -236,8 +183,8 @@ class $ {
|
|
|
236
183
|
this.tasks.remove(e);
|
|
237
184
|
}
|
|
238
185
|
execute(e) {
|
|
239
|
-
const { task:
|
|
240
|
-
return
|
|
186
|
+
const { task: r, resolve: o, reject: n } = e;
|
|
187
|
+
return r().then(o).catch(n).finally(() => {
|
|
241
188
|
this.runningCount--, this._run();
|
|
242
189
|
});
|
|
243
190
|
}
|
|
@@ -248,31 +195,31 @@ class $ {
|
|
|
248
195
|
}
|
|
249
196
|
}
|
|
250
197
|
}
|
|
251
|
-
const
|
|
198
|
+
const F = {
|
|
252
199
|
parallelCount: 4,
|
|
253
200
|
retries: 0,
|
|
254
201
|
createId: () => `${Date.now()}_${Math.random().toString().slice(2, 8)}`
|
|
255
|
-
},
|
|
256
|
-
const e = { ...
|
|
257
|
-
get(
|
|
202
|
+
}, J = (t) => {
|
|
203
|
+
const e = { ...F, ...t }, { parallelCount: r, createId: o, ...n } = e, s = new b(r), i = n.retries > 0 ? M(n) : null, q = {
|
|
204
|
+
get(d, c) {
|
|
258
205
|
return (...u) => {
|
|
259
|
-
const
|
|
260
|
-
return
|
|
206
|
+
const l = f[c](...u), a = o(l), p = () => i ? i.request(l) : Reflect.apply(d[c], d, u);
|
|
207
|
+
return s.add(a, p);
|
|
261
208
|
};
|
|
262
209
|
}
|
|
263
210
|
};
|
|
264
211
|
return {
|
|
265
|
-
requestor: new Proxy(
|
|
266
|
-
concurrentPool:
|
|
212
|
+
requestor: new Proxy(g(), q),
|
|
213
|
+
concurrentPool: s
|
|
267
214
|
};
|
|
268
|
-
},
|
|
269
|
-
cacheRequestor:
|
|
270
|
-
idempotencyRequestor:
|
|
271
|
-
retryRequestor:
|
|
272
|
-
concurrentPoolRequestor:
|
|
215
|
+
}, Q = {
|
|
216
|
+
cacheRequestor: x,
|
|
217
|
+
idempotencyRequestor: H,
|
|
218
|
+
retryRequestor: M,
|
|
219
|
+
concurrentPoolRequestor: J
|
|
273
220
|
};
|
|
274
221
|
export {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
222
|
+
U as inject,
|
|
223
|
+
Q as requestExtender,
|
|
224
|
+
g as useRequestor
|
|
278
225
|
};
|
package/dist/index.umd.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(
|
|
1
|
+
(function(i,a){typeof exports=="object"&&typeof module<"u"?a(exports,require("localforage"),require("id-queue")):typeof define=="function"&&define.amd?define(["exports","localforage","id-queue"],a):(i=typeof globalThis<"u"?globalThis:i||self,a(i["net-vert/core"]={},i.localforage,i.idQueue))})(this,function(i,a,j){"use strict";const p={get:(t,e)=>({url:t,method:"get",...e,params:e==null?void 0:e.params}),post:(t,e,r)=>({url:t,method:"post",data:e,headers:{"Content-Type":"application/json",...r==null?void 0:r.headers},...r}),delete:(t,e)=>({url:t,method:"delete",...e}),put:(t,e,r)=>({url:t,method:"put",data:e,headers:{"Content-Type":"application/json",...r==null?void 0:r.headers},...r}),request:t=>t};function T(t){const e={};return Object.keys(p).forEach(r=>{e[r]=(...s)=>{const n=p[r](...s);return t(n)}}),{...e,request:t}}const R="default",k=new Map,I=(t,e=R)=>{k.set(e,T(t))},f=(t=R)=>{const e=k.get(t);if(!e)throw new Error(`Requestor实例 ${t} 未注册`);return e};class S{constructor(e){e&&a.config(e)}async has(e){return await a.getItem(e)!==null}async get(e){try{return await a.getItem(e)??void 0}catch(r){console.error("Error getting data",r);return}}async set(e,r){return await a.setItem(e,r),r}async delete(e){await a.removeItem(e)}async clear(){await a.clear()}}const E=new S,$=new Map,D=t=>t?E:$,O=()=>{const t=new Map;return{getPromise:o=>t.get(o),setPromise:(o,l)=>{t.set(o,l)},delPromise:o=>{t.delete(o)},clearCache:()=>{t.clear()}}},_=(t,e)=>({value:t,expiresAt:Date.now()+e}),z=t=>t.expiresAt>Date.now(),A={key:t=>t.url,persist:!1,duration:1/0},v=t=>{const e={...A,...t},r=D(e.persist),{getPromise:s,setPromise:n,delPromise:o}=O(),l={get(y,c){return async(...d)=>{const h=p[c](...d),u=e.key(h),C=s(u);if(C)return C;const w=r.get(u);let g=!1;if(w&&z(w)){try{g=e.isValid?await e.isValid({key:u,config:h,cachedData:w}):!0}catch(m){console.error(`校验异常 ${u}`,m)}g||r.delete(u)}if(g)return w.value;const M=Reflect.apply(y[c],y,d).then(async m=>{const N=typeof e.duration=="number"?e.duration:e.duration({key:u,config:h,response:m});return r.set(u,_(m,N)),m}).finally(()=>{o(u)});return n(u,M),M}}};return new Proxy(f(),l)},b=t=>{const{method:e,url:r,params:s,data:n}=t;return[e,r,JSON.stringify(s),JSON.stringify(n)].join("|")},H=t=>v({key:e=>t?t(e):b(e),persist:!1}),V={retries:3,delay:0,retryCondition:()=>!0},x=t=>{const{retries:e,delay:r,retryCondition:s}={...V,...t},n={get(o,l){return(...y)=>{let c=0;const q=()=>Reflect.apply(o[l],o,y).catch(d=>{if(c<e&&s(d)){c++;const h=typeof r=="function"?r(c):r;return new Promise(u=>{setTimeout(()=>u(q()),h)})}return Promise.reject(d)});return q()}}};return new Proxy(f(),n)};class F{constructor(e=4){this.parallelCount=e,this.tasks=new j.TaskQueue,this.runningCount=0}add(e,r){return console.log("poolinset",e,r),new Promise((s,n)=>{this.tasks.enqueue(e,{task:r,resolve:s,reject:n}),this._run()})}remove(e){this.tasks.remove(e)}execute(e){const{task:r,resolve:s,reject:n}=e;return r().then(s).catch(n).finally(()=>{this.runningCount--,this._run()})}_run(){for(;this.runningCount<this.parallelCount&&this.tasks.size>0;){const e=this.tasks.dequeue();this.runningCount++,this.execute(e)}}}const J={parallelCount:4,retries:0,createId:()=>`${Date.now()}_${Math.random().toString().slice(2,8)}`},L={cacheRequestor:v,idempotencyRequestor:H,retryRequestor:x,concurrentPoolRequestor:t=>{const e={...J,...t},{parallelCount:r,createId:s,...n}=e,o=new F(r),l=n.retries>0?x(n):null,P={get(y,c){return(...d)=>{const h=p[c](...d),u=s(h),C=()=>l?l.request(h):Reflect.apply(y[c],y,d);return o.add(u,C)}}};return{requestor:new Proxy(f(),P),concurrentPool:o}}};i.inject=I,i.requestExtender=L,i.useRequestor=f,Object.defineProperty(i,Symbol.toStringTag,{value:"Module"})});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@net-vert/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Dependency Inversion Network Library with Type-Safe Injection.",
|
|
5
5
|
"main": "dist/index",
|
|
6
6
|
"type": "module",
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
"access": "public"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"id-queue": "^1.0.9"
|
|
29
|
+
"id-queue": "^1.0.9",
|
|
30
|
+
"localforage": "^1.10.0"
|
|
30
31
|
},
|
|
31
32
|
"scripts": {
|
|
32
33
|
"test": "vitest",
|