@lytjs/plugin-data 6.5.0
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 +82 -0
- package/dist/index.cjs +427 -0
- package/dist/index.d.cts +131 -0
- package/dist/index.d.ts +131 -0
- package/dist/index.mjs +402 -0
- package/package.json +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# @lytjs/plugin-data
|
|
2
|
+
|
|
3
|
+
LytJS 官方增强版数据获取插件,提供乐观更新、缓存策略、请求去重等功能。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @lytjs/plugin-data
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 快速开始
|
|
12
|
+
|
|
13
|
+
### 作为插件使用
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { createApp } from '@lytjs/core';
|
|
17
|
+
import pluginData from '@lytjs/plugin-data';
|
|
18
|
+
|
|
19
|
+
const app = createApp();
|
|
20
|
+
app.use(pluginData);
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### 独立使用
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { createData } from '@lytjs/plugin-data';
|
|
27
|
+
|
|
28
|
+
// 创建数据实例
|
|
29
|
+
const data = createData('/api/users');
|
|
30
|
+
|
|
31
|
+
// 监听数据变化
|
|
32
|
+
data.on('data', (users) => {
|
|
33
|
+
console.log('数据已更新:', users);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// 手动刷新
|
|
37
|
+
await data.refresh();
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 特性
|
|
41
|
+
|
|
42
|
+
- 乐观更新
|
|
43
|
+
- 请求去重
|
|
44
|
+
- 多种缓存策略(TTL、LRU)
|
|
45
|
+
- 自动重试
|
|
46
|
+
- 与 @lytjs/plugin-data-fetch 深度集成
|
|
47
|
+
- 零外部依赖
|
|
48
|
+
|
|
49
|
+
## API
|
|
50
|
+
|
|
51
|
+
### createData(url, options, globalOptions)
|
|
52
|
+
|
|
53
|
+
创建数据获取实例。
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { createData } from '@lytjs/plugin-data';
|
|
57
|
+
|
|
58
|
+
const data = createData('/api/users', {
|
|
59
|
+
method: 'GET',
|
|
60
|
+
cache: 'ttl',
|
|
61
|
+
ttl: 60000,
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### createDataManager(globalOptions)
|
|
66
|
+
|
|
67
|
+
创建全局数据管理器。
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { createDataManager } from '@lytjs/plugin-data';
|
|
71
|
+
|
|
72
|
+
const manager = createDataManager({
|
|
73
|
+
defaultCache: 'ttl',
|
|
74
|
+
defaultTTL: 60000,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const data = manager.create('/api/users');
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## 许可证
|
|
81
|
+
|
|
82
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
LRUCache: () => LRUCache,
|
|
24
|
+
TTLCache: () => TTLCache,
|
|
25
|
+
createData: () => createData,
|
|
26
|
+
createDataManager: () => createDataManager,
|
|
27
|
+
default: () => index_default,
|
|
28
|
+
generateCacheKey: () => import_plugin_data_fetch.generateCacheKey
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(index_exports);
|
|
31
|
+
var import_core = require("@lytjs/core");
|
|
32
|
+
var import_reactivity = require("@lytjs/reactivity");
|
|
33
|
+
var import_plugin_data_fetch = require("@lytjs/plugin-data-fetch");
|
|
34
|
+
|
|
35
|
+
// src/cache/ttl.ts
|
|
36
|
+
var TTLCache = class {
|
|
37
|
+
constructor() {
|
|
38
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
39
|
+
}
|
|
40
|
+
get(key) {
|
|
41
|
+
const entry = this.cache.get(key);
|
|
42
|
+
if (!entry) return null;
|
|
43
|
+
if (Date.now() > entry.expiresAt) {
|
|
44
|
+
this.cache.delete(key);
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
return entry;
|
|
48
|
+
}
|
|
49
|
+
set(key, value) {
|
|
50
|
+
this.cache.set(key, value);
|
|
51
|
+
}
|
|
52
|
+
delete(key) {
|
|
53
|
+
this.cache.delete(key);
|
|
54
|
+
}
|
|
55
|
+
clear() {
|
|
56
|
+
this.cache.clear();
|
|
57
|
+
}
|
|
58
|
+
has(key) {
|
|
59
|
+
const entry = this.cache.get(key);
|
|
60
|
+
if (!entry) return false;
|
|
61
|
+
if (Date.now() > entry.expiresAt) {
|
|
62
|
+
this.cache.delete(key);
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 获取缓存大小
|
|
69
|
+
*/
|
|
70
|
+
size() {
|
|
71
|
+
return this.cache.size;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 获取所有缓存键
|
|
75
|
+
*/
|
|
76
|
+
keys() {
|
|
77
|
+
return Array.from(this.cache.keys());
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// src/cache/lru.ts
|
|
82
|
+
var LRUCache = class {
|
|
83
|
+
constructor(maxSize = 100) {
|
|
84
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
85
|
+
this.maxSize = maxSize;
|
|
86
|
+
}
|
|
87
|
+
get(key) {
|
|
88
|
+
const entry = this.cache.get(key);
|
|
89
|
+
if (!entry) return null;
|
|
90
|
+
if (Date.now() > entry.expiresAt) {
|
|
91
|
+
this.cache.delete(key);
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
entry.lastAccessed = Date.now();
|
|
95
|
+
this.cache.delete(key);
|
|
96
|
+
this.cache.set(key, entry);
|
|
97
|
+
return entry;
|
|
98
|
+
}
|
|
99
|
+
set(key, value) {
|
|
100
|
+
if (this.cache.size >= this.maxSize) {
|
|
101
|
+
this.evict();
|
|
102
|
+
}
|
|
103
|
+
const lruEntry = {
|
|
104
|
+
...value,
|
|
105
|
+
lastAccessed: Date.now()
|
|
106
|
+
};
|
|
107
|
+
this.cache.set(key, lruEntry);
|
|
108
|
+
}
|
|
109
|
+
delete(key) {
|
|
110
|
+
this.cache.delete(key);
|
|
111
|
+
}
|
|
112
|
+
clear() {
|
|
113
|
+
this.cache.clear();
|
|
114
|
+
}
|
|
115
|
+
has(key) {
|
|
116
|
+
const entry = this.cache.get(key);
|
|
117
|
+
if (!entry) return false;
|
|
118
|
+
if (Date.now() > entry.expiresAt) {
|
|
119
|
+
this.cache.delete(key);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
evict() {
|
|
125
|
+
const keys = Array.from(this.cache.keys());
|
|
126
|
+
if (keys.length > 0) {
|
|
127
|
+
this.cache.delete(keys[0]);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* 获取缓存大小
|
|
132
|
+
*/
|
|
133
|
+
size() {
|
|
134
|
+
return this.cache.size;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* 获取最大缓存大小
|
|
138
|
+
*/
|
|
139
|
+
getMaxSize() {
|
|
140
|
+
return this.maxSize;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// src/index.ts
|
|
145
|
+
var pendingRequests = /* @__PURE__ */ new Map();
|
|
146
|
+
var activeInstances = /* @__PURE__ */ new Set();
|
|
147
|
+
function createData(url, options = {}, globalOptions = {}) {
|
|
148
|
+
const dataSignal = (0, import_reactivity.signal)(null);
|
|
149
|
+
const isLoadingSignal = (0, import_reactivity.signal)(false);
|
|
150
|
+
const errorSignal = (0, import_reactivity.signal)(null);
|
|
151
|
+
const refetchCountSignal = (0, import_reactivity.signal)(0);
|
|
152
|
+
const isPrefetchingSignal = (0, import_reactivity.signal)(false);
|
|
153
|
+
let abortController = null;
|
|
154
|
+
let lastRollbackData = null;
|
|
155
|
+
const state = (0, import_reactivity.signalComputed)(() => ({
|
|
156
|
+
data: dataSignal(),
|
|
157
|
+
isLoading: isLoadingSignal(),
|
|
158
|
+
error: errorSignal(),
|
|
159
|
+
isSuccess: !isLoadingSignal() && dataSignal() !== null && errorSignal() === null,
|
|
160
|
+
isError: !isLoadingSignal() && errorSignal() !== null,
|
|
161
|
+
refetchCount: refetchCountSignal()
|
|
162
|
+
}));
|
|
163
|
+
const {
|
|
164
|
+
baseUrl = globalOptions.baseUrl || "",
|
|
165
|
+
timeout = globalOptions.timeout || 3e4,
|
|
166
|
+
retries = globalOptions.retries || 0,
|
|
167
|
+
retryDelay = globalOptions.retryDelay || 1e3,
|
|
168
|
+
cacheStrategy = globalOptions.defaultCacheStrategy || "no-cache",
|
|
169
|
+
cacheTime = globalOptions.defaultCacheTime || 3e5,
|
|
170
|
+
dedupe = globalOptions.defaultDedupe || false,
|
|
171
|
+
optimisticData,
|
|
172
|
+
prefetch = false
|
|
173
|
+
} = options;
|
|
174
|
+
const cacheStorage = globalOptions.cacheStorage || new import_plugin_data_fetch.DefaultCacheStorage();
|
|
175
|
+
const fullUrl = baseUrl ? baseUrl.endsWith("/") ? baseUrl + url.slice(1) : baseUrl + url : url;
|
|
176
|
+
const cacheKey = (0, import_plugin_data_fetch.generateCacheKey)(fullUrl, options);
|
|
177
|
+
async function executeFetch() {
|
|
178
|
+
if (dedupe) {
|
|
179
|
+
const existing = pendingRequests.get(cacheKey);
|
|
180
|
+
if (existing && Date.now() - existing.timestamp < 1e4) {
|
|
181
|
+
return existing.promise;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (optimisticData !== void 0) {
|
|
185
|
+
optimisticUpdate(optimisticData);
|
|
186
|
+
}
|
|
187
|
+
if (abortController) {
|
|
188
|
+
abortController.abort();
|
|
189
|
+
}
|
|
190
|
+
abortController = new AbortController();
|
|
191
|
+
const abortSignal = abortController.signal;
|
|
192
|
+
const manager = (0, import_plugin_data_fetch.createFetchManager)({
|
|
193
|
+
...globalOptions,
|
|
194
|
+
cacheStorage
|
|
195
|
+
});
|
|
196
|
+
const instance = manager.createFetch(url, {
|
|
197
|
+
...options,
|
|
198
|
+
signal: abortSignal
|
|
199
|
+
});
|
|
200
|
+
const promise = instance.fetch().then(
|
|
201
|
+
(result) => {
|
|
202
|
+
dataSignal.set(result);
|
|
203
|
+
if (globalOptions.storeIntegration?.onUpdate) {
|
|
204
|
+
globalOptions.storeIntegration.onUpdate(cacheKey, result);
|
|
205
|
+
}
|
|
206
|
+
pendingRequests.delete(cacheKey);
|
|
207
|
+
return result;
|
|
208
|
+
},
|
|
209
|
+
(error) => {
|
|
210
|
+
if (lastRollbackData !== null) {
|
|
211
|
+
rollbackOptimistic();
|
|
212
|
+
}
|
|
213
|
+
pendingRequests.delete(cacheKey);
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
);
|
|
217
|
+
if (dedupe) {
|
|
218
|
+
pendingRequests.set(cacheKey, { promise, timestamp: Date.now() });
|
|
219
|
+
}
|
|
220
|
+
activeInstances.add({ cancel: instance.cancel });
|
|
221
|
+
return promise;
|
|
222
|
+
}
|
|
223
|
+
const optimisticUpdate = (data) => {
|
|
224
|
+
lastRollbackData = dataSignal();
|
|
225
|
+
dataSignal.set(data);
|
|
226
|
+
};
|
|
227
|
+
const rollbackOptimistic = () => {
|
|
228
|
+
if (lastRollbackData !== null) {
|
|
229
|
+
dataSignal.set(lastRollbackData);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
const doFetch = async () => {
|
|
233
|
+
return executeFetch();
|
|
234
|
+
};
|
|
235
|
+
const refetch = async () => {
|
|
236
|
+
refetchCountSignal.update((v) => v + 1);
|
|
237
|
+
return executeFetch();
|
|
238
|
+
};
|
|
239
|
+
const cancel = () => {
|
|
240
|
+
if (abortController) {
|
|
241
|
+
abortController.abort();
|
|
242
|
+
abortController = null;
|
|
243
|
+
}
|
|
244
|
+
isLoadingSignal.set(false);
|
|
245
|
+
};
|
|
246
|
+
const setData = (data) => {
|
|
247
|
+
if (typeof data === "function") {
|
|
248
|
+
dataSignal.update((prev) => data(prev));
|
|
249
|
+
} else {
|
|
250
|
+
dataSignal.set(data);
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
const setError = (error) => {
|
|
254
|
+
errorSignal.set(error);
|
|
255
|
+
};
|
|
256
|
+
const reset = () => {
|
|
257
|
+
dataSignal.set(null);
|
|
258
|
+
isLoadingSignal.set(false);
|
|
259
|
+
errorSignal.set(null);
|
|
260
|
+
refetchCountSignal.set(0);
|
|
261
|
+
abortController = null;
|
|
262
|
+
};
|
|
263
|
+
return {
|
|
264
|
+
get state() {
|
|
265
|
+
return state();
|
|
266
|
+
},
|
|
267
|
+
get isPrefetching() {
|
|
268
|
+
return isPrefetchingSignal();
|
|
269
|
+
},
|
|
270
|
+
fetch: doFetch,
|
|
271
|
+
refetch,
|
|
272
|
+
cancel,
|
|
273
|
+
setData,
|
|
274
|
+
setError,
|
|
275
|
+
reset,
|
|
276
|
+
optimisticUpdate,
|
|
277
|
+
rollbackOptimistic
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
function createDataManager(globalOptions = {}) {
|
|
281
|
+
const cacheStorage = globalOptions.cacheStorage || new import_plugin_data_fetch.DefaultCacheStorage();
|
|
282
|
+
const manager = (0, import_plugin_data_fetch.createFetchManager)(globalOptions);
|
|
283
|
+
return {
|
|
284
|
+
createData(url, options = {}) {
|
|
285
|
+
return createData(url, options, {
|
|
286
|
+
...globalOptions,
|
|
287
|
+
cacheStorage
|
|
288
|
+
});
|
|
289
|
+
},
|
|
290
|
+
async get(url, options = {}) {
|
|
291
|
+
const instance = createData(
|
|
292
|
+
url,
|
|
293
|
+
{ ...options, method: "GET" },
|
|
294
|
+
{
|
|
295
|
+
...globalOptions,
|
|
296
|
+
cacheStorage
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
return instance.fetch();
|
|
300
|
+
},
|
|
301
|
+
async post(url, body, options = {}) {
|
|
302
|
+
const instance = createData(
|
|
303
|
+
url,
|
|
304
|
+
{
|
|
305
|
+
...options,
|
|
306
|
+
method: "POST",
|
|
307
|
+
body: body ? typeof body === "string" ? body : JSON.stringify(body) : void 0,
|
|
308
|
+
headers: {
|
|
309
|
+
"Content-Type": "application/json",
|
|
310
|
+
...options.headers
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
...globalOptions,
|
|
315
|
+
cacheStorage
|
|
316
|
+
}
|
|
317
|
+
);
|
|
318
|
+
return instance.fetch();
|
|
319
|
+
},
|
|
320
|
+
async put(url, body, options = {}) {
|
|
321
|
+
const instance = createData(
|
|
322
|
+
url,
|
|
323
|
+
{
|
|
324
|
+
...options,
|
|
325
|
+
method: "PUT",
|
|
326
|
+
body: body ? typeof body === "string" ? body : JSON.stringify(body) : void 0,
|
|
327
|
+
headers: {
|
|
328
|
+
"Content-Type": "application/json",
|
|
329
|
+
...options.headers
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
...globalOptions,
|
|
334
|
+
cacheStorage
|
|
335
|
+
}
|
|
336
|
+
);
|
|
337
|
+
return instance.fetch();
|
|
338
|
+
},
|
|
339
|
+
async delete(url, options = {}) {
|
|
340
|
+
const instance = createData(
|
|
341
|
+
url,
|
|
342
|
+
{ ...options, method: "DELETE" },
|
|
343
|
+
{
|
|
344
|
+
...globalOptions,
|
|
345
|
+
cacheStorage
|
|
346
|
+
}
|
|
347
|
+
);
|
|
348
|
+
return instance.fetch();
|
|
349
|
+
},
|
|
350
|
+
addRequestInterceptor(interceptor) {
|
|
351
|
+
manager.addRequestInterceptor(interceptor);
|
|
352
|
+
},
|
|
353
|
+
addResponseInterceptor(interceptor) {
|
|
354
|
+
manager.addResponseInterceptor(interceptor);
|
|
355
|
+
},
|
|
356
|
+
addErrorInterceptor(interceptor) {
|
|
357
|
+
manager.addErrorInterceptor(interceptor);
|
|
358
|
+
},
|
|
359
|
+
getCacheStorage() {
|
|
360
|
+
return manager.getCacheStorage();
|
|
361
|
+
},
|
|
362
|
+
clearCache() {
|
|
363
|
+
manager.clearCache();
|
|
364
|
+
},
|
|
365
|
+
invalidateCache(key) {
|
|
366
|
+
manager.invalidateCache(key);
|
|
367
|
+
},
|
|
368
|
+
async prefetch(url, options = {}) {
|
|
369
|
+
const instance = createData(
|
|
370
|
+
url,
|
|
371
|
+
{ ...options, prefetch: true },
|
|
372
|
+
{
|
|
373
|
+
...globalOptions,
|
|
374
|
+
cacheStorage
|
|
375
|
+
}
|
|
376
|
+
);
|
|
377
|
+
return instance.fetch();
|
|
378
|
+
},
|
|
379
|
+
getPendingRequests() {
|
|
380
|
+
return new Set(pendingRequests.keys());
|
|
381
|
+
},
|
|
382
|
+
cancelAllRequests() {
|
|
383
|
+
for (const instance of activeInstances) {
|
|
384
|
+
instance.cancel();
|
|
385
|
+
}
|
|
386
|
+
activeInstances.clear();
|
|
387
|
+
pendingRequests.clear();
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
var pluginData = (0, import_core.definePlugin)({
|
|
392
|
+
name: "data",
|
|
393
|
+
version: "6.0.0",
|
|
394
|
+
description: "LytJS official enhanced data plugin with optimistic updates, deduplication, and store integration",
|
|
395
|
+
author: "LytJS Team",
|
|
396
|
+
keywords: ["lytjs", "data", "fetch", "optimistic", "store"],
|
|
397
|
+
schema: {
|
|
398
|
+
type: "object",
|
|
399
|
+
object: {
|
|
400
|
+
properties: {
|
|
401
|
+
baseUrl: { type: "string" },
|
|
402
|
+
timeout: { type: "number", default: 3e4 },
|
|
403
|
+
retries: { type: "number", default: 0 },
|
|
404
|
+
retryDelay: { type: "number", default: 1e3 },
|
|
405
|
+
defaultCacheStrategy: { type: "string", default: "no-cache" },
|
|
406
|
+
defaultCacheTime: { type: "number", default: 3e5 },
|
|
407
|
+
defaultDedupe: { type: "boolean", default: false },
|
|
408
|
+
offlineMode: { type: "boolean", default: false }
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
install(app, options) {
|
|
413
|
+
const dataManager = createDataManager(options);
|
|
414
|
+
app.config.globalProperties.$data = dataManager;
|
|
415
|
+
app.config.globalProperties.$dataManager = dataManager;
|
|
416
|
+
app.provide("lyt-data", dataManager);
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
var index_default = pluginData;
|
|
420
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
421
|
+
0 && (module.exports = {
|
|
422
|
+
LRUCache,
|
|
423
|
+
TTLCache,
|
|
424
|
+
createData,
|
|
425
|
+
createDataManager,
|
|
426
|
+
generateCacheKey
|
|
427
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import * as _lytjs_core from '@lytjs/core';
|
|
2
|
+
import { FetchInstance, RequestOptions as RequestOptions$1, FetchError, CacheStorage, FetchPluginOptions, CacheEntry } from '@lytjs/plugin-data-fetch';
|
|
3
|
+
export { CacheEntry, CacheStorage, FetchError, generateCacheKey } from '@lytjs/plugin-data-fetch';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @lytjs/plugin-data - 类型定义
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
interface RequestOptions extends RequestOptions$1 {
|
|
10
|
+
/** 是否启用请求去重 */
|
|
11
|
+
dedupe?: boolean;
|
|
12
|
+
/** 乐观更新数据 */
|
|
13
|
+
optimisticData?: unknown;
|
|
14
|
+
/** 预取相关配置 */
|
|
15
|
+
prefetch?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface DataPluginOptions extends FetchPluginOptions {
|
|
19
|
+
/** 默认去重设置 */
|
|
20
|
+
defaultDedupe?: boolean;
|
|
21
|
+
/** 离线模式 */
|
|
22
|
+
offlineMode?: boolean;
|
|
23
|
+
/** 与 store 的集成配置 */
|
|
24
|
+
storeIntegration?: {
|
|
25
|
+
/** 数据同步时的钩子 */
|
|
26
|
+
onSync?: (key: string, data: unknown) => void;
|
|
27
|
+
/** 数据更新时的钩子 */
|
|
28
|
+
onUpdate?: (key: string, data: unknown) => void;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
interface DataManager {
|
|
32
|
+
/** 创建数据实例 */
|
|
33
|
+
createData<T = unknown>(url: string, options?: RequestOptions): DataInstance<T>;
|
|
34
|
+
/** 执行简单 GET 请求 */
|
|
35
|
+
get<T = unknown>(url: string, options?: RequestOptions): Promise<T>;
|
|
36
|
+
/** 执行简单 POST 请求 */
|
|
37
|
+
post<T = unknown>(url: string, body?: unknown, options?: RequestOptions): Promise<T>;
|
|
38
|
+
/** 执行简单 PUT 请求 */
|
|
39
|
+
put<T = unknown>(url: string, body?: unknown, options?: RequestOptions): Promise<T>;
|
|
40
|
+
/** 执行简单 DELETE 请求 */
|
|
41
|
+
delete<T = unknown>(url: string, options?: RequestOptions): Promise<T>;
|
|
42
|
+
/** 添加请求拦截器 */
|
|
43
|
+
addRequestInterceptor(interceptor: (config: RequestOptions) => RequestOptions | Promise<RequestOptions>): void;
|
|
44
|
+
/** 添加响应拦截器 */
|
|
45
|
+
addResponseInterceptor(interceptor: <T = unknown>(response: T) => T | Promise<T>): void;
|
|
46
|
+
/** 添加错误拦截器 */
|
|
47
|
+
addErrorInterceptor(interceptor: (error: FetchError) => FetchError | Promise<FetchError>): void;
|
|
48
|
+
/** 获取缓存存储 */
|
|
49
|
+
getCacheStorage(): CacheStorage;
|
|
50
|
+
/** 清空缓存 */
|
|
51
|
+
clearCache(): void;
|
|
52
|
+
/** 清除特定缓存 */
|
|
53
|
+
invalidateCache(key: string): void;
|
|
54
|
+
/** 预取数据 */
|
|
55
|
+
prefetch<T = unknown>(url: string, options?: RequestOptions): Promise<T>;
|
|
56
|
+
/** 获取当前的请求队列 */
|
|
57
|
+
getPendingRequests(): Set<string>;
|
|
58
|
+
/** 取消所有请求 */
|
|
59
|
+
cancelAllRequests(): void;
|
|
60
|
+
}
|
|
61
|
+
interface DataInstance<T = unknown> extends FetchInstance<T> {
|
|
62
|
+
/** 乐观更新数据 */
|
|
63
|
+
optimisticUpdate(data: T): void;
|
|
64
|
+
/** 回滚乐观更新 */
|
|
65
|
+
rollbackOptimistic(): void;
|
|
66
|
+
/** 获取预取状态 */
|
|
67
|
+
isPrefetching: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* TTL (Time To Live) 缓存策略实现
|
|
72
|
+
*/
|
|
73
|
+
|
|
74
|
+
declare class TTLCache implements CacheStorage {
|
|
75
|
+
private cache;
|
|
76
|
+
get<T = unknown>(key: string): CacheEntry<T> | null;
|
|
77
|
+
set<T = unknown>(key: string, value: CacheEntry<T>): void;
|
|
78
|
+
delete(key: string): void;
|
|
79
|
+
clear(): void;
|
|
80
|
+
has(key: string): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* 获取缓存大小
|
|
83
|
+
*/
|
|
84
|
+
size(): number;
|
|
85
|
+
/**
|
|
86
|
+
* 获取所有缓存键
|
|
87
|
+
*/
|
|
88
|
+
keys(): string[];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* LRU (Least Recently Used) 缓存策略实现
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
interface LRUEntry<T = unknown> extends CacheEntry<T> {
|
|
96
|
+
lastAccessed: number;
|
|
97
|
+
}
|
|
98
|
+
declare class LRUCache implements CacheStorage {
|
|
99
|
+
private cache;
|
|
100
|
+
private readonly maxSize;
|
|
101
|
+
constructor(maxSize?: number);
|
|
102
|
+
get<T = unknown>(key: string): LRUEntry<T> | null;
|
|
103
|
+
set<T = unknown>(key: string, value: CacheEntry<T>): void;
|
|
104
|
+
delete(key: string): void;
|
|
105
|
+
clear(): void;
|
|
106
|
+
has(key: string): boolean;
|
|
107
|
+
private evict;
|
|
108
|
+
/**
|
|
109
|
+
* 获取缓存大小
|
|
110
|
+
*/
|
|
111
|
+
size(): number;
|
|
112
|
+
/**
|
|
113
|
+
* 获取最大缓存大小
|
|
114
|
+
*/
|
|
115
|
+
getMaxSize(): number;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 创建数据实例
|
|
120
|
+
*/
|
|
121
|
+
declare function createData<T = unknown>(url: string, options?: RequestOptions, globalOptions?: DataPluginOptions): DataInstance<T>;
|
|
122
|
+
/**
|
|
123
|
+
* 创建数据管理器
|
|
124
|
+
*/
|
|
125
|
+
declare function createDataManager(globalOptions?: DataPluginOptions): DataManager;
|
|
126
|
+
/**
|
|
127
|
+
* 定义插件
|
|
128
|
+
*/
|
|
129
|
+
declare const pluginData: _lytjs_core.PluginDefinition<unknown>;
|
|
130
|
+
|
|
131
|
+
export { type DataInstance, type DataManager, type DataPluginOptions, LRUCache, type RequestOptions, TTLCache, createData, createDataManager, pluginData as default };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import * as _lytjs_core from '@lytjs/core';
|
|
2
|
+
import { FetchInstance, RequestOptions as RequestOptions$1, FetchError, CacheStorage, FetchPluginOptions, CacheEntry } from '@lytjs/plugin-data-fetch';
|
|
3
|
+
export { CacheEntry, CacheStorage, FetchError, generateCacheKey } from '@lytjs/plugin-data-fetch';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @lytjs/plugin-data - 类型定义
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
interface RequestOptions extends RequestOptions$1 {
|
|
10
|
+
/** 是否启用请求去重 */
|
|
11
|
+
dedupe?: boolean;
|
|
12
|
+
/** 乐观更新数据 */
|
|
13
|
+
optimisticData?: unknown;
|
|
14
|
+
/** 预取相关配置 */
|
|
15
|
+
prefetch?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface DataPluginOptions extends FetchPluginOptions {
|
|
19
|
+
/** 默认去重设置 */
|
|
20
|
+
defaultDedupe?: boolean;
|
|
21
|
+
/** 离线模式 */
|
|
22
|
+
offlineMode?: boolean;
|
|
23
|
+
/** 与 store 的集成配置 */
|
|
24
|
+
storeIntegration?: {
|
|
25
|
+
/** 数据同步时的钩子 */
|
|
26
|
+
onSync?: (key: string, data: unknown) => void;
|
|
27
|
+
/** 数据更新时的钩子 */
|
|
28
|
+
onUpdate?: (key: string, data: unknown) => void;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
interface DataManager {
|
|
32
|
+
/** 创建数据实例 */
|
|
33
|
+
createData<T = unknown>(url: string, options?: RequestOptions): DataInstance<T>;
|
|
34
|
+
/** 执行简单 GET 请求 */
|
|
35
|
+
get<T = unknown>(url: string, options?: RequestOptions): Promise<T>;
|
|
36
|
+
/** 执行简单 POST 请求 */
|
|
37
|
+
post<T = unknown>(url: string, body?: unknown, options?: RequestOptions): Promise<T>;
|
|
38
|
+
/** 执行简单 PUT 请求 */
|
|
39
|
+
put<T = unknown>(url: string, body?: unknown, options?: RequestOptions): Promise<T>;
|
|
40
|
+
/** 执行简单 DELETE 请求 */
|
|
41
|
+
delete<T = unknown>(url: string, options?: RequestOptions): Promise<T>;
|
|
42
|
+
/** 添加请求拦截器 */
|
|
43
|
+
addRequestInterceptor(interceptor: (config: RequestOptions) => RequestOptions | Promise<RequestOptions>): void;
|
|
44
|
+
/** 添加响应拦截器 */
|
|
45
|
+
addResponseInterceptor(interceptor: <T = unknown>(response: T) => T | Promise<T>): void;
|
|
46
|
+
/** 添加错误拦截器 */
|
|
47
|
+
addErrorInterceptor(interceptor: (error: FetchError) => FetchError | Promise<FetchError>): void;
|
|
48
|
+
/** 获取缓存存储 */
|
|
49
|
+
getCacheStorage(): CacheStorage;
|
|
50
|
+
/** 清空缓存 */
|
|
51
|
+
clearCache(): void;
|
|
52
|
+
/** 清除特定缓存 */
|
|
53
|
+
invalidateCache(key: string): void;
|
|
54
|
+
/** 预取数据 */
|
|
55
|
+
prefetch<T = unknown>(url: string, options?: RequestOptions): Promise<T>;
|
|
56
|
+
/** 获取当前的请求队列 */
|
|
57
|
+
getPendingRequests(): Set<string>;
|
|
58
|
+
/** 取消所有请求 */
|
|
59
|
+
cancelAllRequests(): void;
|
|
60
|
+
}
|
|
61
|
+
interface DataInstance<T = unknown> extends FetchInstance<T> {
|
|
62
|
+
/** 乐观更新数据 */
|
|
63
|
+
optimisticUpdate(data: T): void;
|
|
64
|
+
/** 回滚乐观更新 */
|
|
65
|
+
rollbackOptimistic(): void;
|
|
66
|
+
/** 获取预取状态 */
|
|
67
|
+
isPrefetching: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* TTL (Time To Live) 缓存策略实现
|
|
72
|
+
*/
|
|
73
|
+
|
|
74
|
+
declare class TTLCache implements CacheStorage {
|
|
75
|
+
private cache;
|
|
76
|
+
get<T = unknown>(key: string): CacheEntry<T> | null;
|
|
77
|
+
set<T = unknown>(key: string, value: CacheEntry<T>): void;
|
|
78
|
+
delete(key: string): void;
|
|
79
|
+
clear(): void;
|
|
80
|
+
has(key: string): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* 获取缓存大小
|
|
83
|
+
*/
|
|
84
|
+
size(): number;
|
|
85
|
+
/**
|
|
86
|
+
* 获取所有缓存键
|
|
87
|
+
*/
|
|
88
|
+
keys(): string[];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* LRU (Least Recently Used) 缓存策略实现
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
interface LRUEntry<T = unknown> extends CacheEntry<T> {
|
|
96
|
+
lastAccessed: number;
|
|
97
|
+
}
|
|
98
|
+
declare class LRUCache implements CacheStorage {
|
|
99
|
+
private cache;
|
|
100
|
+
private readonly maxSize;
|
|
101
|
+
constructor(maxSize?: number);
|
|
102
|
+
get<T = unknown>(key: string): LRUEntry<T> | null;
|
|
103
|
+
set<T = unknown>(key: string, value: CacheEntry<T>): void;
|
|
104
|
+
delete(key: string): void;
|
|
105
|
+
clear(): void;
|
|
106
|
+
has(key: string): boolean;
|
|
107
|
+
private evict;
|
|
108
|
+
/**
|
|
109
|
+
* 获取缓存大小
|
|
110
|
+
*/
|
|
111
|
+
size(): number;
|
|
112
|
+
/**
|
|
113
|
+
* 获取最大缓存大小
|
|
114
|
+
*/
|
|
115
|
+
getMaxSize(): number;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 创建数据实例
|
|
120
|
+
*/
|
|
121
|
+
declare function createData<T = unknown>(url: string, options?: RequestOptions, globalOptions?: DataPluginOptions): DataInstance<T>;
|
|
122
|
+
/**
|
|
123
|
+
* 创建数据管理器
|
|
124
|
+
*/
|
|
125
|
+
declare function createDataManager(globalOptions?: DataPluginOptions): DataManager;
|
|
126
|
+
/**
|
|
127
|
+
* 定义插件
|
|
128
|
+
*/
|
|
129
|
+
declare const pluginData: _lytjs_core.PluginDefinition<unknown>;
|
|
130
|
+
|
|
131
|
+
export { type DataInstance, type DataManager, type DataPluginOptions, LRUCache, type RequestOptions, TTLCache, createData, createDataManager, pluginData as default };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { definePlugin } from "@lytjs/core";
|
|
3
|
+
import { signal, signalComputed as computed } from "@lytjs/reactivity";
|
|
4
|
+
import {
|
|
5
|
+
createFetchManager,
|
|
6
|
+
DefaultCacheStorage,
|
|
7
|
+
generateCacheKey
|
|
8
|
+
} from "@lytjs/plugin-data-fetch";
|
|
9
|
+
|
|
10
|
+
// src/cache/ttl.ts
|
|
11
|
+
var TTLCache = class {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
14
|
+
}
|
|
15
|
+
get(key) {
|
|
16
|
+
const entry = this.cache.get(key);
|
|
17
|
+
if (!entry) return null;
|
|
18
|
+
if (Date.now() > entry.expiresAt) {
|
|
19
|
+
this.cache.delete(key);
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return entry;
|
|
23
|
+
}
|
|
24
|
+
set(key, value) {
|
|
25
|
+
this.cache.set(key, value);
|
|
26
|
+
}
|
|
27
|
+
delete(key) {
|
|
28
|
+
this.cache.delete(key);
|
|
29
|
+
}
|
|
30
|
+
clear() {
|
|
31
|
+
this.cache.clear();
|
|
32
|
+
}
|
|
33
|
+
has(key) {
|
|
34
|
+
const entry = this.cache.get(key);
|
|
35
|
+
if (!entry) return false;
|
|
36
|
+
if (Date.now() > entry.expiresAt) {
|
|
37
|
+
this.cache.delete(key);
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 获取缓存大小
|
|
44
|
+
*/
|
|
45
|
+
size() {
|
|
46
|
+
return this.cache.size;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 获取所有缓存键
|
|
50
|
+
*/
|
|
51
|
+
keys() {
|
|
52
|
+
return Array.from(this.cache.keys());
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// src/cache/lru.ts
|
|
57
|
+
var LRUCache = class {
|
|
58
|
+
constructor(maxSize = 100) {
|
|
59
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
60
|
+
this.maxSize = maxSize;
|
|
61
|
+
}
|
|
62
|
+
get(key) {
|
|
63
|
+
const entry = this.cache.get(key);
|
|
64
|
+
if (!entry) return null;
|
|
65
|
+
if (Date.now() > entry.expiresAt) {
|
|
66
|
+
this.cache.delete(key);
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
entry.lastAccessed = Date.now();
|
|
70
|
+
this.cache.delete(key);
|
|
71
|
+
this.cache.set(key, entry);
|
|
72
|
+
return entry;
|
|
73
|
+
}
|
|
74
|
+
set(key, value) {
|
|
75
|
+
if (this.cache.size >= this.maxSize) {
|
|
76
|
+
this.evict();
|
|
77
|
+
}
|
|
78
|
+
const lruEntry = {
|
|
79
|
+
...value,
|
|
80
|
+
lastAccessed: Date.now()
|
|
81
|
+
};
|
|
82
|
+
this.cache.set(key, lruEntry);
|
|
83
|
+
}
|
|
84
|
+
delete(key) {
|
|
85
|
+
this.cache.delete(key);
|
|
86
|
+
}
|
|
87
|
+
clear() {
|
|
88
|
+
this.cache.clear();
|
|
89
|
+
}
|
|
90
|
+
has(key) {
|
|
91
|
+
const entry = this.cache.get(key);
|
|
92
|
+
if (!entry) return false;
|
|
93
|
+
if (Date.now() > entry.expiresAt) {
|
|
94
|
+
this.cache.delete(key);
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
evict() {
|
|
100
|
+
const keys = Array.from(this.cache.keys());
|
|
101
|
+
if (keys.length > 0) {
|
|
102
|
+
this.cache.delete(keys[0]);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 获取缓存大小
|
|
107
|
+
*/
|
|
108
|
+
size() {
|
|
109
|
+
return this.cache.size;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* 获取最大缓存大小
|
|
113
|
+
*/
|
|
114
|
+
getMaxSize() {
|
|
115
|
+
return this.maxSize;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// src/index.ts
|
|
120
|
+
var pendingRequests = /* @__PURE__ */ new Map();
|
|
121
|
+
var activeInstances = /* @__PURE__ */ new Set();
|
|
122
|
+
function createData(url, options = {}, globalOptions = {}) {
|
|
123
|
+
const dataSignal = signal(null);
|
|
124
|
+
const isLoadingSignal = signal(false);
|
|
125
|
+
const errorSignal = signal(null);
|
|
126
|
+
const refetchCountSignal = signal(0);
|
|
127
|
+
const isPrefetchingSignal = signal(false);
|
|
128
|
+
let abortController = null;
|
|
129
|
+
let lastRollbackData = null;
|
|
130
|
+
const state = computed(() => ({
|
|
131
|
+
data: dataSignal(),
|
|
132
|
+
isLoading: isLoadingSignal(),
|
|
133
|
+
error: errorSignal(),
|
|
134
|
+
isSuccess: !isLoadingSignal() && dataSignal() !== null && errorSignal() === null,
|
|
135
|
+
isError: !isLoadingSignal() && errorSignal() !== null,
|
|
136
|
+
refetchCount: refetchCountSignal()
|
|
137
|
+
}));
|
|
138
|
+
const {
|
|
139
|
+
baseUrl = globalOptions.baseUrl || "",
|
|
140
|
+
timeout = globalOptions.timeout || 3e4,
|
|
141
|
+
retries = globalOptions.retries || 0,
|
|
142
|
+
retryDelay = globalOptions.retryDelay || 1e3,
|
|
143
|
+
cacheStrategy = globalOptions.defaultCacheStrategy || "no-cache",
|
|
144
|
+
cacheTime = globalOptions.defaultCacheTime || 3e5,
|
|
145
|
+
dedupe = globalOptions.defaultDedupe || false,
|
|
146
|
+
optimisticData,
|
|
147
|
+
prefetch = false
|
|
148
|
+
} = options;
|
|
149
|
+
const cacheStorage = globalOptions.cacheStorage || new DefaultCacheStorage();
|
|
150
|
+
const fullUrl = baseUrl ? baseUrl.endsWith("/") ? baseUrl + url.slice(1) : baseUrl + url : url;
|
|
151
|
+
const cacheKey = generateCacheKey(fullUrl, options);
|
|
152
|
+
async function executeFetch() {
|
|
153
|
+
if (dedupe) {
|
|
154
|
+
const existing = pendingRequests.get(cacheKey);
|
|
155
|
+
if (existing && Date.now() - existing.timestamp < 1e4) {
|
|
156
|
+
return existing.promise;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (optimisticData !== void 0) {
|
|
160
|
+
optimisticUpdate(optimisticData);
|
|
161
|
+
}
|
|
162
|
+
if (abortController) {
|
|
163
|
+
abortController.abort();
|
|
164
|
+
}
|
|
165
|
+
abortController = new AbortController();
|
|
166
|
+
const abortSignal = abortController.signal;
|
|
167
|
+
const manager = createFetchManager({
|
|
168
|
+
...globalOptions,
|
|
169
|
+
cacheStorage
|
|
170
|
+
});
|
|
171
|
+
const instance = manager.createFetch(url, {
|
|
172
|
+
...options,
|
|
173
|
+
signal: abortSignal
|
|
174
|
+
});
|
|
175
|
+
const promise = instance.fetch().then(
|
|
176
|
+
(result) => {
|
|
177
|
+
dataSignal.set(result);
|
|
178
|
+
if (globalOptions.storeIntegration?.onUpdate) {
|
|
179
|
+
globalOptions.storeIntegration.onUpdate(cacheKey, result);
|
|
180
|
+
}
|
|
181
|
+
pendingRequests.delete(cacheKey);
|
|
182
|
+
return result;
|
|
183
|
+
},
|
|
184
|
+
(error) => {
|
|
185
|
+
if (lastRollbackData !== null) {
|
|
186
|
+
rollbackOptimistic();
|
|
187
|
+
}
|
|
188
|
+
pendingRequests.delete(cacheKey);
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
);
|
|
192
|
+
if (dedupe) {
|
|
193
|
+
pendingRequests.set(cacheKey, { promise, timestamp: Date.now() });
|
|
194
|
+
}
|
|
195
|
+
activeInstances.add({ cancel: instance.cancel });
|
|
196
|
+
return promise;
|
|
197
|
+
}
|
|
198
|
+
const optimisticUpdate = (data) => {
|
|
199
|
+
lastRollbackData = dataSignal();
|
|
200
|
+
dataSignal.set(data);
|
|
201
|
+
};
|
|
202
|
+
const rollbackOptimistic = () => {
|
|
203
|
+
if (lastRollbackData !== null) {
|
|
204
|
+
dataSignal.set(lastRollbackData);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
const doFetch = async () => {
|
|
208
|
+
return executeFetch();
|
|
209
|
+
};
|
|
210
|
+
const refetch = async () => {
|
|
211
|
+
refetchCountSignal.update((v) => v + 1);
|
|
212
|
+
return executeFetch();
|
|
213
|
+
};
|
|
214
|
+
const cancel = () => {
|
|
215
|
+
if (abortController) {
|
|
216
|
+
abortController.abort();
|
|
217
|
+
abortController = null;
|
|
218
|
+
}
|
|
219
|
+
isLoadingSignal.set(false);
|
|
220
|
+
};
|
|
221
|
+
const setData = (data) => {
|
|
222
|
+
if (typeof data === "function") {
|
|
223
|
+
dataSignal.update((prev) => data(prev));
|
|
224
|
+
} else {
|
|
225
|
+
dataSignal.set(data);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
const setError = (error) => {
|
|
229
|
+
errorSignal.set(error);
|
|
230
|
+
};
|
|
231
|
+
const reset = () => {
|
|
232
|
+
dataSignal.set(null);
|
|
233
|
+
isLoadingSignal.set(false);
|
|
234
|
+
errorSignal.set(null);
|
|
235
|
+
refetchCountSignal.set(0);
|
|
236
|
+
abortController = null;
|
|
237
|
+
};
|
|
238
|
+
return {
|
|
239
|
+
get state() {
|
|
240
|
+
return state();
|
|
241
|
+
},
|
|
242
|
+
get isPrefetching() {
|
|
243
|
+
return isPrefetchingSignal();
|
|
244
|
+
},
|
|
245
|
+
fetch: doFetch,
|
|
246
|
+
refetch,
|
|
247
|
+
cancel,
|
|
248
|
+
setData,
|
|
249
|
+
setError,
|
|
250
|
+
reset,
|
|
251
|
+
optimisticUpdate,
|
|
252
|
+
rollbackOptimistic
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
function createDataManager(globalOptions = {}) {
|
|
256
|
+
const cacheStorage = globalOptions.cacheStorage || new DefaultCacheStorage();
|
|
257
|
+
const manager = createFetchManager(globalOptions);
|
|
258
|
+
return {
|
|
259
|
+
createData(url, options = {}) {
|
|
260
|
+
return createData(url, options, {
|
|
261
|
+
...globalOptions,
|
|
262
|
+
cacheStorage
|
|
263
|
+
});
|
|
264
|
+
},
|
|
265
|
+
async get(url, options = {}) {
|
|
266
|
+
const instance = createData(
|
|
267
|
+
url,
|
|
268
|
+
{ ...options, method: "GET" },
|
|
269
|
+
{
|
|
270
|
+
...globalOptions,
|
|
271
|
+
cacheStorage
|
|
272
|
+
}
|
|
273
|
+
);
|
|
274
|
+
return instance.fetch();
|
|
275
|
+
},
|
|
276
|
+
async post(url, body, options = {}) {
|
|
277
|
+
const instance = createData(
|
|
278
|
+
url,
|
|
279
|
+
{
|
|
280
|
+
...options,
|
|
281
|
+
method: "POST",
|
|
282
|
+
body: body ? typeof body === "string" ? body : JSON.stringify(body) : void 0,
|
|
283
|
+
headers: {
|
|
284
|
+
"Content-Type": "application/json",
|
|
285
|
+
...options.headers
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
...globalOptions,
|
|
290
|
+
cacheStorage
|
|
291
|
+
}
|
|
292
|
+
);
|
|
293
|
+
return instance.fetch();
|
|
294
|
+
},
|
|
295
|
+
async put(url, body, options = {}) {
|
|
296
|
+
const instance = createData(
|
|
297
|
+
url,
|
|
298
|
+
{
|
|
299
|
+
...options,
|
|
300
|
+
method: "PUT",
|
|
301
|
+
body: body ? typeof body === "string" ? body : JSON.stringify(body) : void 0,
|
|
302
|
+
headers: {
|
|
303
|
+
"Content-Type": "application/json",
|
|
304
|
+
...options.headers
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
...globalOptions,
|
|
309
|
+
cacheStorage
|
|
310
|
+
}
|
|
311
|
+
);
|
|
312
|
+
return instance.fetch();
|
|
313
|
+
},
|
|
314
|
+
async delete(url, options = {}) {
|
|
315
|
+
const instance = createData(
|
|
316
|
+
url,
|
|
317
|
+
{ ...options, method: "DELETE" },
|
|
318
|
+
{
|
|
319
|
+
...globalOptions,
|
|
320
|
+
cacheStorage
|
|
321
|
+
}
|
|
322
|
+
);
|
|
323
|
+
return instance.fetch();
|
|
324
|
+
},
|
|
325
|
+
addRequestInterceptor(interceptor) {
|
|
326
|
+
manager.addRequestInterceptor(interceptor);
|
|
327
|
+
},
|
|
328
|
+
addResponseInterceptor(interceptor) {
|
|
329
|
+
manager.addResponseInterceptor(interceptor);
|
|
330
|
+
},
|
|
331
|
+
addErrorInterceptor(interceptor) {
|
|
332
|
+
manager.addErrorInterceptor(interceptor);
|
|
333
|
+
},
|
|
334
|
+
getCacheStorage() {
|
|
335
|
+
return manager.getCacheStorage();
|
|
336
|
+
},
|
|
337
|
+
clearCache() {
|
|
338
|
+
manager.clearCache();
|
|
339
|
+
},
|
|
340
|
+
invalidateCache(key) {
|
|
341
|
+
manager.invalidateCache(key);
|
|
342
|
+
},
|
|
343
|
+
async prefetch(url, options = {}) {
|
|
344
|
+
const instance = createData(
|
|
345
|
+
url,
|
|
346
|
+
{ ...options, prefetch: true },
|
|
347
|
+
{
|
|
348
|
+
...globalOptions,
|
|
349
|
+
cacheStorage
|
|
350
|
+
}
|
|
351
|
+
);
|
|
352
|
+
return instance.fetch();
|
|
353
|
+
},
|
|
354
|
+
getPendingRequests() {
|
|
355
|
+
return new Set(pendingRequests.keys());
|
|
356
|
+
},
|
|
357
|
+
cancelAllRequests() {
|
|
358
|
+
for (const instance of activeInstances) {
|
|
359
|
+
instance.cancel();
|
|
360
|
+
}
|
|
361
|
+
activeInstances.clear();
|
|
362
|
+
pendingRequests.clear();
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
var pluginData = definePlugin({
|
|
367
|
+
name: "data",
|
|
368
|
+
version: "6.0.0",
|
|
369
|
+
description: "LytJS official enhanced data plugin with optimistic updates, deduplication, and store integration",
|
|
370
|
+
author: "LytJS Team",
|
|
371
|
+
keywords: ["lytjs", "data", "fetch", "optimistic", "store"],
|
|
372
|
+
schema: {
|
|
373
|
+
type: "object",
|
|
374
|
+
object: {
|
|
375
|
+
properties: {
|
|
376
|
+
baseUrl: { type: "string" },
|
|
377
|
+
timeout: { type: "number", default: 3e4 },
|
|
378
|
+
retries: { type: "number", default: 0 },
|
|
379
|
+
retryDelay: { type: "number", default: 1e3 },
|
|
380
|
+
defaultCacheStrategy: { type: "string", default: "no-cache" },
|
|
381
|
+
defaultCacheTime: { type: "number", default: 3e5 },
|
|
382
|
+
defaultDedupe: { type: "boolean", default: false },
|
|
383
|
+
offlineMode: { type: "boolean", default: false }
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
},
|
|
387
|
+
install(app, options) {
|
|
388
|
+
const dataManager = createDataManager(options);
|
|
389
|
+
app.config.globalProperties.$data = dataManager;
|
|
390
|
+
app.config.globalProperties.$dataManager = dataManager;
|
|
391
|
+
app.provide("lyt-data", dataManager);
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
var index_default = pluginData;
|
|
395
|
+
export {
|
|
396
|
+
LRUCache,
|
|
397
|
+
TTLCache,
|
|
398
|
+
createData,
|
|
399
|
+
createDataManager,
|
|
400
|
+
index_default as default,
|
|
401
|
+
generateCacheKey
|
|
402
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/package.json",
|
|
3
|
+
"name": "@lytjs/plugin-data",
|
|
4
|
+
"version": "6.5.0",
|
|
5
|
+
"description": "LytJS official enhanced data plugin with optimistic updates, deduplication, and store integration",
|
|
6
|
+
"author": "lytjs",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "./dist/index.cjs",
|
|
10
|
+
"module": "./dist/index.mjs",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.mjs",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
},
|
|
18
|
+
"./package.json": "./package.json"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"scripts": {
|
|
27
|
+
"dev": "tsup --watch",
|
|
28
|
+
"build": "tsup",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:watch": "vitest",
|
|
31
|
+
"test:coverage": "vitest run --coverage",
|
|
32
|
+
"type-check": "tsc --noEmit",
|
|
33
|
+
"clean": "rm -rf dist node_modules .turbo"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@lytjs/core": "^6.5.0",
|
|
37
|
+
"@lytjs/reactivity": "^6.5.0",
|
|
38
|
+
"@lytjs/plugin-data-fetch": "^6.5.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"tsup": "^8.3.6",
|
|
42
|
+
"typescript": "^5.7.3",
|
|
43
|
+
"vitest": "^3.0.0"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public",
|
|
48
|
+
"registry": "https://registry.npmjs.org/"
|
|
49
|
+
},
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "https://gitee.com/lytjs/lytjs.git",
|
|
53
|
+
"directory": "packages/plugins/packages/plugin-data"
|
|
54
|
+
},
|
|
55
|
+
"keywords": [
|
|
56
|
+
"lytjs",
|
|
57
|
+
"data",
|
|
58
|
+
"fetch",
|
|
59
|
+
"optimistic",
|
|
60
|
+
"store"
|
|
61
|
+
],
|
|
62
|
+
"homepage": "https://gitee.com/lytjs/lytjs/tree/develop/packages/plugins/packages/plugin-data#readme",
|
|
63
|
+
"bugs": {
|
|
64
|
+
"url": "https://gitee.com/lytjs/lytjs/issues"
|
|
65
|
+
}
|
|
66
|
+
}
|