@vobs/resource 1.1.0 → 1.2.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/dist/boundary.cjs +708 -0
- package/dist/boundary.cjs.map +1 -0
- package/dist/boundary.d.cts +25 -0
- package/dist/boundary.d.ts +25 -0
- package/dist/boundary.js +705 -0
- package/dist/boundary.js.map +1 -0
- package/dist/index.cjs +1239 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +1229 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.cjs +885 -0
- package/dist/plugin.cjs.map +1 -0
- package/dist/plugin.d.cts +23 -0
- package/dist/plugin.d.ts +23 -0
- package/dist/plugin.js +881 -0
- package/dist/plugin.js.map +1 -0
- package/dist/resource.cjs +832 -0
- package/dist/resource.cjs.map +1 -0
- package/dist/resource.d.cts +67 -0
- package/dist/resource.d.ts +67 -0
- package/dist/resource.js +827 -0
- package/dist/resource.js.map +1 -0
- package/package.json +27 -8
package/dist/resource.js
ADDED
|
@@ -0,0 +1,827 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
4
|
+
// packages/reactivity/src/debug.ts
|
|
5
|
+
var activeDebugHooks = null;
|
|
6
|
+
var signalNames = /* @__PURE__ */ new WeakMap();
|
|
7
|
+
function hasDebugHooks() {
|
|
8
|
+
return activeDebugHooks !== null;
|
|
9
|
+
}
|
|
10
|
+
__name(hasDebugHooks, "hasDebugHooks");
|
|
11
|
+
function setSignalDebugName(signal, name) {
|
|
12
|
+
signalNames.set(signal, name);
|
|
13
|
+
invokeDebug("signalNamed", signal, name);
|
|
14
|
+
}
|
|
15
|
+
__name(setSignalDebugName, "setSignalDebugName");
|
|
16
|
+
function invokeDebug(name, ...args) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
__name(invokeDebug, "invokeDebug");
|
|
20
|
+
|
|
21
|
+
// packages/reactivity/src/owner.ts
|
|
22
|
+
var currentOwner = null;
|
|
23
|
+
var nextOwnerId = 1;
|
|
24
|
+
function createOwner() {
|
|
25
|
+
const parent = currentOwner;
|
|
26
|
+
let disposed = parent?.disposed ?? false;
|
|
27
|
+
const children = [];
|
|
28
|
+
const cleanups = [];
|
|
29
|
+
const errorHandlers = /* @__PURE__ */ new Set();
|
|
30
|
+
const owner = {
|
|
31
|
+
id: `owner-${nextOwnerId++}`,
|
|
32
|
+
parent,
|
|
33
|
+
children,
|
|
34
|
+
depth: (parent?.depth ?? -1) + 1,
|
|
35
|
+
get disposed() {
|
|
36
|
+
return disposed;
|
|
37
|
+
},
|
|
38
|
+
run(fn) {
|
|
39
|
+
if (disposed) throw new Error("Vobs: \u5DF2\u9500\u6BC1\u7684 Owner \u4E0D\u80FD\u7EE7\u7EED\u8FD0\u884C");
|
|
40
|
+
const previous = currentOwner;
|
|
41
|
+
currentOwner = owner;
|
|
42
|
+
try {
|
|
43
|
+
return fn();
|
|
44
|
+
} finally {
|
|
45
|
+
currentOwner = previous;
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
addCleanup(cleanup) {
|
|
49
|
+
if (disposed) {
|
|
50
|
+
cleanup();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
cleanups.push(cleanup);
|
|
54
|
+
},
|
|
55
|
+
onDispose(cleanup) {
|
|
56
|
+
owner.addCleanup(cleanup);
|
|
57
|
+
},
|
|
58
|
+
onError(handler) {
|
|
59
|
+
errorHandlers.add(handler);
|
|
60
|
+
const remove = /* @__PURE__ */ __name(() => errorHandlers.delete(handler), "remove");
|
|
61
|
+
owner.addCleanup(remove);
|
|
62
|
+
return remove;
|
|
63
|
+
},
|
|
64
|
+
handleError(error) {
|
|
65
|
+
for (const handler of [...errorHandlers].reverse()) {
|
|
66
|
+
try {
|
|
67
|
+
handler(error);
|
|
68
|
+
return true;
|
|
69
|
+
} catch (handlerError) {
|
|
70
|
+
return parent?.handleError(handlerError) ?? false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return parent?.handleError(error) ?? false;
|
|
74
|
+
},
|
|
75
|
+
dispose() {
|
|
76
|
+
if (disposed) return;
|
|
77
|
+
disposed = true;
|
|
78
|
+
for (const child of [...children]) child.dispose();
|
|
79
|
+
children.length = 0;
|
|
80
|
+
let firstError;
|
|
81
|
+
for (let index = cleanups.length - 1; index >= 0; index--) {
|
|
82
|
+
try {
|
|
83
|
+
cleanups[index]();
|
|
84
|
+
} catch (error) {
|
|
85
|
+
firstError ?? (firstError = error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
cleanups.length = 0;
|
|
89
|
+
if (parent) {
|
|
90
|
+
const index = parent.children.indexOf(owner);
|
|
91
|
+
if (index >= 0) parent.children.splice(index, 1);
|
|
92
|
+
}
|
|
93
|
+
if (firstError) throw firstError;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
if (parent && !parent.disposed) parent.children.push(owner);
|
|
97
|
+
return owner;
|
|
98
|
+
}
|
|
99
|
+
__name(createOwner, "createOwner");
|
|
100
|
+
function getCurrentOwner() {
|
|
101
|
+
return currentOwner;
|
|
102
|
+
}
|
|
103
|
+
__name(getCurrentOwner, "getCurrentOwner");
|
|
104
|
+
|
|
105
|
+
// packages/reactivity/src/signal.ts
|
|
106
|
+
var currentSubscriber = null;
|
|
107
|
+
function getCurrentSubscriber() {
|
|
108
|
+
return currentSubscriber;
|
|
109
|
+
}
|
|
110
|
+
__name(getCurrentSubscriber, "getCurrentSubscriber");
|
|
111
|
+
function setCurrentSubscriber(subscriber) {
|
|
112
|
+
currentSubscriber = subscriber;
|
|
113
|
+
}
|
|
114
|
+
__name(setCurrentSubscriber, "setCurrentSubscriber");
|
|
115
|
+
function trackDependency(dependency) {
|
|
116
|
+
if (!currentSubscriber || currentSubscriber.disposed) return;
|
|
117
|
+
!currentSubscriber.dependencies.has(dependency);
|
|
118
|
+
currentSubscriber.dependencies.add(dependency);
|
|
119
|
+
}
|
|
120
|
+
__name(trackDependency, "trackDependency");
|
|
121
|
+
function state(initialValue, debugName) {
|
|
122
|
+
let value = initialValue;
|
|
123
|
+
let disposed = false;
|
|
124
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
125
|
+
const signalInstance = {
|
|
126
|
+
get value() {
|
|
127
|
+
const subscriber = getCurrentSubscriber();
|
|
128
|
+
if (subscriber && !subscriber.disposed) {
|
|
129
|
+
subscribers.add(subscriber);
|
|
130
|
+
trackDependency(signalInstance);
|
|
131
|
+
}
|
|
132
|
+
return value;
|
|
133
|
+
},
|
|
134
|
+
set value(nextValue) {
|
|
135
|
+
if (disposed || Object.is(value, nextValue)) return;
|
|
136
|
+
value = nextValue;
|
|
137
|
+
for (const subscriber of [...subscribers]) subscriber.notify();
|
|
138
|
+
},
|
|
139
|
+
unsubscribe(subscriber) {
|
|
140
|
+
subscribers.delete(subscriber);
|
|
141
|
+
},
|
|
142
|
+
// 与 `.value =` 赋值同一条路径:判等短路、debug hook、notify 全部一致。
|
|
143
|
+
// 以闭包实现,可安全地作为回调直接传递(无 this 绑定问题)。
|
|
144
|
+
set(next) {
|
|
145
|
+
signalInstance.value = next;
|
|
146
|
+
},
|
|
147
|
+
dispose() {
|
|
148
|
+
if (disposed) return;
|
|
149
|
+
disposed = true;
|
|
150
|
+
subscribers.clear();
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
const owner = getCurrentOwner();
|
|
154
|
+
owner?.addCleanup(signalInstance.dispose);
|
|
155
|
+
if (debugName?.trim()) setSignalDebugName(signalInstance, debugName.trim());
|
|
156
|
+
return signalInstance;
|
|
157
|
+
}
|
|
158
|
+
__name(state, "state");
|
|
159
|
+
|
|
160
|
+
// packages/reactivity/src/scheduler.ts
|
|
161
|
+
var _Scheduler = class _Scheduler {
|
|
162
|
+
constructor() {
|
|
163
|
+
this.dirtyEffects = /* @__PURE__ */ new Set();
|
|
164
|
+
this.lowPriorityEffects = /* @__PURE__ */ new Set();
|
|
165
|
+
// flush 不可重入(flushing 标志保证),缓冲数组可在轮次间安全复用,避免每轮分配。
|
|
166
|
+
this.normalBuffer = [];
|
|
167
|
+
this.lowBuffer = [];
|
|
168
|
+
this.flushing = false;
|
|
169
|
+
this.scheduled = false;
|
|
170
|
+
this.batchDepth = 0;
|
|
171
|
+
}
|
|
172
|
+
schedule(effect2) {
|
|
173
|
+
if (effect2.disposed) return;
|
|
174
|
+
this.dirtyEffects.add(effect2);
|
|
175
|
+
this.lowPriorityEffects.delete(effect2);
|
|
176
|
+
this.ensureScheduled();
|
|
177
|
+
}
|
|
178
|
+
/** Queue an effect behind normal updates while preserving deterministic order. */
|
|
179
|
+
scheduleLow(effect2) {
|
|
180
|
+
if (effect2.disposed) return;
|
|
181
|
+
if (!this.dirtyEffects.has(effect2)) this.lowPriorityEffects.add(effect2);
|
|
182
|
+
this.ensureScheduled();
|
|
183
|
+
}
|
|
184
|
+
ensureScheduled() {
|
|
185
|
+
if (this.batchDepth === 0 && !this.flushing && !this.scheduled) {
|
|
186
|
+
this.scheduled = true;
|
|
187
|
+
queueMicrotask(() => {
|
|
188
|
+
this.scheduled = false;
|
|
189
|
+
this.flush();
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
remove(effect2) {
|
|
194
|
+
this.dirtyEffects.delete(effect2);
|
|
195
|
+
this.lowPriorityEffects.delete(effect2);
|
|
196
|
+
}
|
|
197
|
+
batch(fn) {
|
|
198
|
+
this.batchDepth++;
|
|
199
|
+
try {
|
|
200
|
+
return fn();
|
|
201
|
+
} finally {
|
|
202
|
+
this.batchDepth--;
|
|
203
|
+
if (this.batchDepth === 0) this.flush();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
flush() {
|
|
207
|
+
if (this.flushing || this.batchDepth > 0) return;
|
|
208
|
+
this.flushing = true;
|
|
209
|
+
let rounds = 0;
|
|
210
|
+
let firstError;
|
|
211
|
+
let hasError = false;
|
|
212
|
+
try {
|
|
213
|
+
while (this.dirtyEffects.size > 0 || this.lowPriorityEffects.size > 0) {
|
|
214
|
+
if (++rounds > 100) {
|
|
215
|
+
this.dirtyEffects.clear();
|
|
216
|
+
this.lowPriorityEffects.clear();
|
|
217
|
+
throw new Error("Vobs: \u54CD\u5E94\u5F0F\u66F4\u65B0\u8D85\u8FC7 100 \u8F6E\uFF0C\u53EF\u80FD\u5B58\u5728\u5FAA\u73AF\u4F9D\u8D56");
|
|
218
|
+
}
|
|
219
|
+
this.collectRunnable(this.dirtyEffects, this.normalBuffer);
|
|
220
|
+
this.collectRunnable(this.lowPriorityEffects, this.lowBuffer);
|
|
221
|
+
sortEffects(this.normalBuffer);
|
|
222
|
+
sortEffects(this.lowBuffer);
|
|
223
|
+
for (const effect2 of this.normalBuffer) {
|
|
224
|
+
try {
|
|
225
|
+
effect2.run();
|
|
226
|
+
} catch (error) {
|
|
227
|
+
if (!hasError) {
|
|
228
|
+
firstError = error;
|
|
229
|
+
hasError = true;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
for (const effect2 of this.lowBuffer) {
|
|
234
|
+
try {
|
|
235
|
+
effect2.run();
|
|
236
|
+
} catch (error) {
|
|
237
|
+
if (!hasError) {
|
|
238
|
+
firstError = error;
|
|
239
|
+
hasError = true;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
this.normalBuffer.length = 0;
|
|
244
|
+
this.lowBuffer.length = 0;
|
|
245
|
+
}
|
|
246
|
+
} finally {
|
|
247
|
+
this.normalBuffer.length = 0;
|
|
248
|
+
this.lowBuffer.length = 0;
|
|
249
|
+
this.flushing = false;
|
|
250
|
+
}
|
|
251
|
+
if (hasError) throw firstError;
|
|
252
|
+
}
|
|
253
|
+
/** 收集未 disposed 的 effect 并清空源集合;run() 期间新调度的 effect 留给下一轮。 */
|
|
254
|
+
collectRunnable(source, target) {
|
|
255
|
+
for (const effect2 of source) {
|
|
256
|
+
if (!effect2.disposed) target.push(effect2);
|
|
257
|
+
}
|
|
258
|
+
source.clear();
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
__name(_Scheduler, "Scheduler");
|
|
262
|
+
var Scheduler = _Scheduler;
|
|
263
|
+
function sortEffects(effects) {
|
|
264
|
+
if (effects.length > 1) {
|
|
265
|
+
effects.sort((a, b) => b.depth - a.depth || a.order - b.order);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
__name(sortEffects, "sortEffects");
|
|
269
|
+
var scheduler = new Scheduler();
|
|
270
|
+
|
|
271
|
+
// packages/reactivity/src/effect.ts
|
|
272
|
+
var nextEffectOrder = 1;
|
|
273
|
+
function cleanupDependencies(subscriber) {
|
|
274
|
+
for (const dependency of subscriber.dependencies) {
|
|
275
|
+
dependency.unsubscribe(subscriber);
|
|
276
|
+
}
|
|
277
|
+
subscriber.dependencies.clear();
|
|
278
|
+
}
|
|
279
|
+
__name(cleanupDependencies, "cleanupDependencies");
|
|
280
|
+
function effect(callback) {
|
|
281
|
+
const owner = getCurrentOwner();
|
|
282
|
+
let cleanup;
|
|
283
|
+
let dirty = true;
|
|
284
|
+
let disposed = false;
|
|
285
|
+
const eff = {
|
|
286
|
+
order: nextEffectOrder++,
|
|
287
|
+
depth: owner?.depth ?? 0,
|
|
288
|
+
dependencies: /* @__PURE__ */ new Set(),
|
|
289
|
+
get disposed() {
|
|
290
|
+
return disposed;
|
|
291
|
+
},
|
|
292
|
+
notify() {
|
|
293
|
+
if (disposed || dirty) return;
|
|
294
|
+
dirty = true;
|
|
295
|
+
scheduler.schedule(eff);
|
|
296
|
+
},
|
|
297
|
+
run() {
|
|
298
|
+
if (disposed || !dirty) return;
|
|
299
|
+
dirty = false;
|
|
300
|
+
const previousCleanup = cleanup;
|
|
301
|
+
cleanup = void 0;
|
|
302
|
+
let cleanupError;
|
|
303
|
+
if (previousCleanup) {
|
|
304
|
+
try {
|
|
305
|
+
previousCleanup();
|
|
306
|
+
} catch (error) {
|
|
307
|
+
const handled2 = owner?.handleError(error) ?? false;
|
|
308
|
+
if (!handled2) cleanupError = error;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
cleanupDependencies(eff);
|
|
312
|
+
const previous = getCurrentSubscriber();
|
|
313
|
+
setCurrentSubscriber(eff);
|
|
314
|
+
let thrown;
|
|
315
|
+
let handled = false;
|
|
316
|
+
try {
|
|
317
|
+
const result = owner ? owner.run(callback) : callback();
|
|
318
|
+
cleanup = typeof result === "function" ? result : void 0;
|
|
319
|
+
} catch (error) {
|
|
320
|
+
thrown = error;
|
|
321
|
+
handled = owner?.handleError(error) ?? false;
|
|
322
|
+
if (!handled) throw error;
|
|
323
|
+
} finally {
|
|
324
|
+
setCurrentSubscriber(previous);
|
|
325
|
+
}
|
|
326
|
+
if (cleanupError && !thrown) throw cleanupError;
|
|
327
|
+
},
|
|
328
|
+
scheduleLow() {
|
|
329
|
+
if (disposed || dirty) return;
|
|
330
|
+
dirty = true;
|
|
331
|
+
scheduler.scheduleLow(eff);
|
|
332
|
+
},
|
|
333
|
+
dispose() {
|
|
334
|
+
if (disposed) return;
|
|
335
|
+
disposed = true;
|
|
336
|
+
dirty = false;
|
|
337
|
+
scheduler.remove(eff);
|
|
338
|
+
const previousCleanup = cleanup;
|
|
339
|
+
cleanup = void 0;
|
|
340
|
+
let cleanupError;
|
|
341
|
+
if (previousCleanup) {
|
|
342
|
+
try {
|
|
343
|
+
previousCleanup();
|
|
344
|
+
} catch (error) {
|
|
345
|
+
const handled = owner?.handleError(error) ?? false;
|
|
346
|
+
if (!handled) cleanupError = error;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
cleanupDependencies(eff);
|
|
350
|
+
if (cleanupError) throw cleanupError;
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
owner?.addCleanup(eff.dispose);
|
|
354
|
+
eff.run();
|
|
355
|
+
return eff;
|
|
356
|
+
}
|
|
357
|
+
__name(effect, "effect");
|
|
358
|
+
|
|
359
|
+
// packages/resource/src/resource.ts
|
|
360
|
+
var defaultClient = createResourceClient();
|
|
361
|
+
function resource(optionsOrFetcher) {
|
|
362
|
+
return typeof optionsOrFetcher === "function" ? defaultClient.resource(optionsOrFetcher) : defaultClient.resource(optionsOrFetcher);
|
|
363
|
+
}
|
|
364
|
+
__name(resource, "resource");
|
|
365
|
+
function createResourceClient(options = {}) {
|
|
366
|
+
let owner = createOwner();
|
|
367
|
+
const cache = /* @__PURE__ */ new Map();
|
|
368
|
+
const entries = /* @__PURE__ */ new Set();
|
|
369
|
+
const defaultStaleTime = validateStaleTime(options.staleTime ?? 0);
|
|
370
|
+
const defaultRetry = validateRetry(options.retry ?? 0);
|
|
371
|
+
const defaultRetryDelay = options.retryDelay ?? 0;
|
|
372
|
+
function createEntry(key, staleTime) {
|
|
373
|
+
const entry = owner.run(() => ({
|
|
374
|
+
key,
|
|
375
|
+
data: state(null),
|
|
376
|
+
error: state(null),
|
|
377
|
+
loading: state(false),
|
|
378
|
+
staleTime,
|
|
379
|
+
subscribers: /* @__PURE__ */ new Set(),
|
|
380
|
+
updatedAt: 0,
|
|
381
|
+
revision: 0,
|
|
382
|
+
inFlight: null,
|
|
383
|
+
controller: null
|
|
384
|
+
}));
|
|
385
|
+
owner.onDispose(() => entry.controller?.abort());
|
|
386
|
+
entries.add(entry);
|
|
387
|
+
return entry;
|
|
388
|
+
}
|
|
389
|
+
__name(createEntry, "createEntry");
|
|
390
|
+
function execute(entry, fetcher, retry, retryDelay) {
|
|
391
|
+
if (entry.inFlight) return entry.inFlight;
|
|
392
|
+
entry.loading.value = true;
|
|
393
|
+
entry.error.value = null;
|
|
394
|
+
const controller = new AbortController();
|
|
395
|
+
entry.controller = controller;
|
|
396
|
+
const revision = entry.revision;
|
|
397
|
+
const request = requestWithRetry(fetcher, controller.signal, retry, retryDelay);
|
|
398
|
+
entry.inFlight = request.then(
|
|
399
|
+
(data) => {
|
|
400
|
+
if (entry.revision === revision) {
|
|
401
|
+
entry.revision++;
|
|
402
|
+
entry.data.value = data;
|
|
403
|
+
entry.error.value = null;
|
|
404
|
+
entry.updatedAt = Date.now();
|
|
405
|
+
}
|
|
406
|
+
return data;
|
|
407
|
+
},
|
|
408
|
+
(reason) => {
|
|
409
|
+
const error = toError(reason);
|
|
410
|
+
if (entry.revision === revision) {
|
|
411
|
+
entry.error.value = error;
|
|
412
|
+
if (!controller.signal.aborted) options.onError?.(error, entry.key);
|
|
413
|
+
}
|
|
414
|
+
throw error;
|
|
415
|
+
}
|
|
416
|
+
).finally(() => {
|
|
417
|
+
entry.loading.value = false;
|
|
418
|
+
if (entry.controller === controller) entry.controller = null;
|
|
419
|
+
entry.inFlight = null;
|
|
420
|
+
});
|
|
421
|
+
return entry.inFlight;
|
|
422
|
+
}
|
|
423
|
+
__name(execute, "execute");
|
|
424
|
+
function isFresh(entry) {
|
|
425
|
+
return entry.updatedAt > 0 && Date.now() - entry.updatedAt <= entry.staleTime;
|
|
426
|
+
}
|
|
427
|
+
__name(isFresh, "isFresh");
|
|
428
|
+
function createResource(optionsOrFetcher) {
|
|
429
|
+
const config = normalizeOptions(optionsOrFetcher, defaultStaleTime, defaultRetry, defaultRetryDelay);
|
|
430
|
+
if (isReactiveKey(config.key)) return createReactiveResource(config);
|
|
431
|
+
const staticKey = resolveKey(config.key);
|
|
432
|
+
const keyId = config.cache && staticKey ? stableSerialize(staticKey) : void 0;
|
|
433
|
+
let entry = keyId ? cache.get(keyId) : void 0;
|
|
434
|
+
if (!entry) {
|
|
435
|
+
entry = createEntry(staticKey, config.staleTime);
|
|
436
|
+
if (keyId) cache.set(keyId, entry);
|
|
437
|
+
}
|
|
438
|
+
const request = /* @__PURE__ */ __name((force) => {
|
|
439
|
+
if (entry.inFlight) return entry.inFlight;
|
|
440
|
+
if (!force && config.strategy === "stale-while-revalidate" && entry.data.value !== null) {
|
|
441
|
+
if (!isFresh(entry)) void execute(entry, config.fetcher, config.retry, config.retryDelay).catch(() => void 0);
|
|
442
|
+
return Promise.resolve(entry.data.value);
|
|
443
|
+
}
|
|
444
|
+
if (!force && isFresh(entry)) return Promise.resolve(entry.data.value);
|
|
445
|
+
return execute(entry, config.fetcher, config.retry, config.retryDelay);
|
|
446
|
+
}, "request");
|
|
447
|
+
void request(false).catch(() => void 0);
|
|
448
|
+
const resourceHandle = {};
|
|
449
|
+
entry.subscribers.add(resourceHandle);
|
|
450
|
+
const dispose = /* @__PURE__ */ __name(() => {
|
|
451
|
+
if (!entry?.subscribers.delete(resourceHandle)) return;
|
|
452
|
+
if (entry.subscribers.size === 0) entry.controller?.abort();
|
|
453
|
+
}, "dispose");
|
|
454
|
+
getCurrentOwner()?.onDispose(dispose);
|
|
455
|
+
return {
|
|
456
|
+
key: staticKey,
|
|
457
|
+
data: entry.data,
|
|
458
|
+
error: entry.error,
|
|
459
|
+
loading: entry.loading,
|
|
460
|
+
dispose,
|
|
461
|
+
refetch: /* @__PURE__ */ __name(() => request(true), "refetch"),
|
|
462
|
+
prefetch: /* @__PURE__ */ __name(() => request(false), "prefetch"),
|
|
463
|
+
invalidate: /* @__PURE__ */ __name(() => {
|
|
464
|
+
entry.updatedAt = 0;
|
|
465
|
+
}, "invalidate"),
|
|
466
|
+
mutate(next) {
|
|
467
|
+
const value = typeof next === "function" ? next(entry.data.value) : next;
|
|
468
|
+
entry.revision++;
|
|
469
|
+
entry.data.value = value;
|
|
470
|
+
entry.error.value = null;
|
|
471
|
+
entry.updatedAt = Date.now();
|
|
472
|
+
},
|
|
473
|
+
optimistic(next, action) {
|
|
474
|
+
const previous = {
|
|
475
|
+
data: entry.data.value,
|
|
476
|
+
error: entry.error.value,
|
|
477
|
+
updatedAt: entry.updatedAt
|
|
478
|
+
};
|
|
479
|
+
const value = typeof next === "function" ? next(entry.data.value) : next;
|
|
480
|
+
const revision = ++entry.revision;
|
|
481
|
+
entry.data.value = value;
|
|
482
|
+
entry.error.value = null;
|
|
483
|
+
entry.updatedAt = Date.now();
|
|
484
|
+
let actionResult;
|
|
485
|
+
try {
|
|
486
|
+
actionResult = Promise.resolve(action());
|
|
487
|
+
} catch (reason) {
|
|
488
|
+
actionResult = Promise.reject(reason);
|
|
489
|
+
}
|
|
490
|
+
return actionResult.catch((reason) => {
|
|
491
|
+
const error = toError(reason);
|
|
492
|
+
if (entry.revision === revision) {
|
|
493
|
+
entry.revision++;
|
|
494
|
+
entry.data.value = previous.data;
|
|
495
|
+
entry.error.value = error;
|
|
496
|
+
entry.updatedAt = previous.updatedAt;
|
|
497
|
+
options.onError?.(error, entry.key);
|
|
498
|
+
}
|
|
499
|
+
throw error;
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
function createReactiveResource(reactiveConfig) {
|
|
504
|
+
const data = state(null);
|
|
505
|
+
const error = state(null);
|
|
506
|
+
const loading = state(false);
|
|
507
|
+
const handle = {};
|
|
508
|
+
let activeEntry;
|
|
509
|
+
let activeKey;
|
|
510
|
+
let disposed = false;
|
|
511
|
+
const sync = /* @__PURE__ */ __name(() => {
|
|
512
|
+
if (!activeEntry) return;
|
|
513
|
+
data.value = activeEntry.data.value;
|
|
514
|
+
error.value = activeEntry.error.value;
|
|
515
|
+
loading.value = activeEntry.loading.value;
|
|
516
|
+
}, "sync");
|
|
517
|
+
const switchKey = /* @__PURE__ */ __name((nextKey) => {
|
|
518
|
+
const keyId2 = reactiveConfig.cache ? stableSerialize(nextKey) : void 0;
|
|
519
|
+
let nextEntry = keyId2 ? cache.get(keyId2) : void 0;
|
|
520
|
+
if (!nextEntry) {
|
|
521
|
+
nextEntry = createEntry(nextKey, reactiveConfig.staleTime);
|
|
522
|
+
if (keyId2) cache.set(keyId2, nextEntry);
|
|
523
|
+
}
|
|
524
|
+
if (activeEntry === nextEntry) {
|
|
525
|
+
sync();
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
if (activeEntry) {
|
|
529
|
+
activeEntry.subscribers.delete(handle);
|
|
530
|
+
if (activeEntry.subscribers.size === 0) activeEntry.controller?.abort();
|
|
531
|
+
}
|
|
532
|
+
activeEntry = nextEntry;
|
|
533
|
+
activeKey = nextKey;
|
|
534
|
+
activeEntry.subscribers.add(handle);
|
|
535
|
+
sync();
|
|
536
|
+
void request2(false).catch(() => void 0);
|
|
537
|
+
}, "switchKey");
|
|
538
|
+
const stop = effect(() => {
|
|
539
|
+
if (disposed) return;
|
|
540
|
+
const nextKey = resolveKey(reactiveConfig.key);
|
|
541
|
+
if (!nextKey) throw new Error("resource: \u54CD\u5E94\u5F0F key \u4E0D\u80FD\u662F undefined");
|
|
542
|
+
switchKey(nextKey);
|
|
543
|
+
sync();
|
|
544
|
+
});
|
|
545
|
+
const dispose2 = /* @__PURE__ */ __name(() => {
|
|
546
|
+
if (disposed) return;
|
|
547
|
+
disposed = true;
|
|
548
|
+
stop.dispose();
|
|
549
|
+
if (activeEntry) {
|
|
550
|
+
activeEntry.subscribers.delete(handle);
|
|
551
|
+
if (activeEntry.subscribers.size === 0) activeEntry.controller?.abort();
|
|
552
|
+
}
|
|
553
|
+
activeEntry = void 0;
|
|
554
|
+
}, "dispose");
|
|
555
|
+
getCurrentOwner()?.onDispose(dispose2);
|
|
556
|
+
return {
|
|
557
|
+
get key() {
|
|
558
|
+
return activeKey;
|
|
559
|
+
},
|
|
560
|
+
data,
|
|
561
|
+
error,
|
|
562
|
+
loading,
|
|
563
|
+
dispose: dispose2,
|
|
564
|
+
refetch: /* @__PURE__ */ __name(() => request2(true), "refetch"),
|
|
565
|
+
prefetch: /* @__PURE__ */ __name(() => request2(false), "prefetch"),
|
|
566
|
+
invalidate: /* @__PURE__ */ __name(() => {
|
|
567
|
+
if (activeEntry) activeEntry.updatedAt = 0;
|
|
568
|
+
}, "invalidate"),
|
|
569
|
+
mutate(next) {
|
|
570
|
+
if (!activeEntry) return;
|
|
571
|
+
const value = typeof next === "function" ? next(activeEntry.data.value) : next;
|
|
572
|
+
activeEntry.revision++;
|
|
573
|
+
activeEntry.data.value = value;
|
|
574
|
+
activeEntry.error.value = null;
|
|
575
|
+
activeEntry.updatedAt = Date.now();
|
|
576
|
+
},
|
|
577
|
+
optimistic(next, action) {
|
|
578
|
+
if (!activeEntry) return Promise.reject(new Error("resource: key \u5C1A\u672A\u521D\u59CB\u5316"));
|
|
579
|
+
return createOptimistic(activeEntry, next, action);
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
function request2(force) {
|
|
583
|
+
if (!activeEntry) return Promise.reject(new Error("resource: key \u5C1A\u672A\u521D\u59CB\u5316"));
|
|
584
|
+
if (!force && isFresh(activeEntry) && reactiveConfig.strategy === "cache-first") {
|
|
585
|
+
return Promise.resolve(activeEntry.data.value);
|
|
586
|
+
}
|
|
587
|
+
if (!force && reactiveConfig.strategy === "stale-while-revalidate" && activeEntry.data.value !== null) {
|
|
588
|
+
if (!isFresh(activeEntry)) void execute(activeEntry, reactiveConfig.fetcher, reactiveConfig.retry, reactiveConfig.retryDelay).catch(() => void 0);
|
|
589
|
+
return Promise.resolve(activeEntry.data.value);
|
|
590
|
+
}
|
|
591
|
+
return execute(activeEntry, reactiveConfig.fetcher, reactiveConfig.retry, reactiveConfig.retryDelay);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
function createOptimistic(target, next, action) {
|
|
595
|
+
const previous = { data: target.data.value, error: target.error.value, updatedAt: target.updatedAt };
|
|
596
|
+
const value = typeof next === "function" ? next(target.data.value) : next;
|
|
597
|
+
const revision = ++target.revision;
|
|
598
|
+
target.data.value = value;
|
|
599
|
+
target.error.value = null;
|
|
600
|
+
target.updatedAt = Date.now();
|
|
601
|
+
return Promise.resolve().then(action).catch((reason) => {
|
|
602
|
+
const failure = toError(reason);
|
|
603
|
+
if (target.revision === revision) {
|
|
604
|
+
target.revision++;
|
|
605
|
+
target.data.value = previous.data;
|
|
606
|
+
target.error.value = failure;
|
|
607
|
+
target.updatedAt = previous.updatedAt;
|
|
608
|
+
options.onError?.(failure, target.key);
|
|
609
|
+
}
|
|
610
|
+
throw failure;
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
__name(createResource, "createResource");
|
|
615
|
+
return {
|
|
616
|
+
resource: createResource,
|
|
617
|
+
invalidate(key) {
|
|
618
|
+
const entry = cache.get(stableSerialize(key));
|
|
619
|
+
if (entry) entry.updatedAt = 0;
|
|
620
|
+
},
|
|
621
|
+
async prefetchAll() {
|
|
622
|
+
const requests = [...entries].map((entry) => entry.inFlight).filter((request) => request !== null);
|
|
623
|
+
await Promise.allSettled(requests);
|
|
624
|
+
},
|
|
625
|
+
dehydrate() {
|
|
626
|
+
const entries2 = [];
|
|
627
|
+
for (const entry of cache.values()) {
|
|
628
|
+
if (entry.updatedAt <= 0 || entry.error.value) continue;
|
|
629
|
+
entries2.push({
|
|
630
|
+
key: entry.key ?? [],
|
|
631
|
+
data: entry.data.value,
|
|
632
|
+
updatedAt: entry.updatedAt,
|
|
633
|
+
staleTime: entry.staleTime
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
return { version: 1, entries: entries2 };
|
|
637
|
+
},
|
|
638
|
+
hydrate(snapshot) {
|
|
639
|
+
for (const restored of parseDehydratedState(snapshot).entries) {
|
|
640
|
+
const keyId = stableSerialize(restored.key);
|
|
641
|
+
const entry = cache.get(keyId) ?? createEntry(restored.key, restored.staleTime);
|
|
642
|
+
entry.data.value = restored.data;
|
|
643
|
+
entry.error.value = null;
|
|
644
|
+
entry.loading.value = false;
|
|
645
|
+
entry.updatedAt = restored.updatedAt;
|
|
646
|
+
entry.revision++;
|
|
647
|
+
cache.set(keyId, entry);
|
|
648
|
+
}
|
|
649
|
+
},
|
|
650
|
+
get(key) {
|
|
651
|
+
const entry = cache.get(stableSerialize(key));
|
|
652
|
+
if (!entry) return void 0;
|
|
653
|
+
return {
|
|
654
|
+
data: entry.data.value,
|
|
655
|
+
error: entry.error.value,
|
|
656
|
+
loading: entry.loading.value,
|
|
657
|
+
updatedAt: entry.updatedAt
|
|
658
|
+
};
|
|
659
|
+
},
|
|
660
|
+
clear() {
|
|
661
|
+
cache.clear();
|
|
662
|
+
entries.clear();
|
|
663
|
+
owner.dispose();
|
|
664
|
+
owner = createOwner();
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
__name(createResourceClient, "createResourceClient");
|
|
669
|
+
function normalizeOptions(optionsOrFetcher, defaultStaleTime, defaultRetry, defaultRetryDelay) {
|
|
670
|
+
if (typeof optionsOrFetcher === "function") {
|
|
671
|
+
return {
|
|
672
|
+
fetcher: optionsOrFetcher,
|
|
673
|
+
staleTime: defaultStaleTime,
|
|
674
|
+
cache: false,
|
|
675
|
+
strategy: "cache-first",
|
|
676
|
+
retry: defaultRetry,
|
|
677
|
+
retryDelay: defaultRetryDelay
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
return {
|
|
681
|
+
key: optionsOrFetcher.key,
|
|
682
|
+
fetcher: optionsOrFetcher.fetcher,
|
|
683
|
+
staleTime: validateStaleTime(optionsOrFetcher.staleTime ?? defaultStaleTime),
|
|
684
|
+
cache: optionsOrFetcher.cache ?? Boolean(optionsOrFetcher.key),
|
|
685
|
+
strategy: optionsOrFetcher.strategy ?? "cache-first",
|
|
686
|
+
retry: validateRetry(optionsOrFetcher.retry ?? defaultRetry),
|
|
687
|
+
retryDelay: optionsOrFetcher.retryDelay ?? defaultRetryDelay
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
__name(normalizeOptions, "normalizeOptions");
|
|
691
|
+
function isReactiveKey(key) {
|
|
692
|
+
return typeof key === "function" || isSignal(key);
|
|
693
|
+
}
|
|
694
|
+
__name(isReactiveKey, "isReactiveKey");
|
|
695
|
+
function isSignal(value) {
|
|
696
|
+
return value !== null && typeof value === "object" && "value" in value && typeof value.dispose === "function";
|
|
697
|
+
}
|
|
698
|
+
__name(isSignal, "isSignal");
|
|
699
|
+
function resolveKey(source) {
|
|
700
|
+
const key = typeof source === "function" ? source() : isSignal(source) ? source.value : source;
|
|
701
|
+
if (key === void 0) return void 0;
|
|
702
|
+
if (!Array.isArray(key)) throw new Error("resource: key \u5FC5\u987B\u662F\u6570\u7EC4");
|
|
703
|
+
return key;
|
|
704
|
+
}
|
|
705
|
+
__name(resolveKey, "resolveKey");
|
|
706
|
+
function requestWithRetry(fetcher, signal, retry, retryDelay) {
|
|
707
|
+
let attempt = 0;
|
|
708
|
+
const request = /* @__PURE__ */ __name(() => Promise.resolve().then(() => fetcher(signal)).catch((reason) => {
|
|
709
|
+
const error = toError(reason);
|
|
710
|
+
if (signal.aborted) throw error;
|
|
711
|
+
if (attempt++ >= retry) throw error;
|
|
712
|
+
const delay = resolveRetryDelay(retryDelay, attempt, error);
|
|
713
|
+
return delay > 0 ? wait(delay).then(request) : request();
|
|
714
|
+
}), "request");
|
|
715
|
+
return request();
|
|
716
|
+
}
|
|
717
|
+
__name(requestWithRetry, "requestWithRetry");
|
|
718
|
+
function resolveRetryDelay(retryDelay, attempt, error) {
|
|
719
|
+
const delay = typeof retryDelay === "function" ? retryDelay(attempt, error) : retryDelay;
|
|
720
|
+
if (!Number.isFinite(delay) || delay < 0) {
|
|
721
|
+
throw new Error("resource: retryDelay \u5FC5\u987B\u662F\u5927\u4E8E\u7B49\u4E8E 0 \u7684\u6709\u9650\u6570\u5B57");
|
|
722
|
+
}
|
|
723
|
+
return delay;
|
|
724
|
+
}
|
|
725
|
+
__name(resolveRetryDelay, "resolveRetryDelay");
|
|
726
|
+
function wait(delay) {
|
|
727
|
+
return new Promise((resolve) => setTimeout(resolve, delay));
|
|
728
|
+
}
|
|
729
|
+
__name(wait, "wait");
|
|
730
|
+
function validateStaleTime(staleTime) {
|
|
731
|
+
if (!Number.isFinite(staleTime) || staleTime < 0) {
|
|
732
|
+
throw new Error("resource: staleTime \u5FC5\u987B\u662F\u5927\u4E8E\u7B49\u4E8E 0 \u7684\u6709\u9650\u6570\u5B57");
|
|
733
|
+
}
|
|
734
|
+
return staleTime;
|
|
735
|
+
}
|
|
736
|
+
__name(validateStaleTime, "validateStaleTime");
|
|
737
|
+
function validateRetry(retry) {
|
|
738
|
+
if (!Number.isInteger(retry) || retry < 0) {
|
|
739
|
+
throw new Error("resource: retry \u5FC5\u987B\u662F\u5927\u4E8E\u7B49\u4E8E 0 \u7684\u6574\u6570");
|
|
740
|
+
}
|
|
741
|
+
return retry;
|
|
742
|
+
}
|
|
743
|
+
__name(validateRetry, "validateRetry");
|
|
744
|
+
function toError(reason) {
|
|
745
|
+
return reason instanceof Error ? reason : new Error(String(reason));
|
|
746
|
+
}
|
|
747
|
+
__name(toError, "toError");
|
|
748
|
+
function stableSerialize(value) {
|
|
749
|
+
return serialize(value, /* @__PURE__ */ new Set());
|
|
750
|
+
}
|
|
751
|
+
__name(stableSerialize, "stableSerialize");
|
|
752
|
+
function serializeResourceState(snapshot) {
|
|
753
|
+
const serialized = JSON.stringify(snapshot);
|
|
754
|
+
return serialized.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
755
|
+
}
|
|
756
|
+
__name(serializeResourceState, "serializeResourceState");
|
|
757
|
+
function parseDehydratedState(snapshot) {
|
|
758
|
+
let value = snapshot;
|
|
759
|
+
if (typeof snapshot === "string") {
|
|
760
|
+
try {
|
|
761
|
+
value = JSON.parse(snapshot);
|
|
762
|
+
} catch {
|
|
763
|
+
throw new Error("resource: \u9884\u53D6\u72B6\u6001\u4E0D\u662F\u6709\u6548 JSON");
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
if (!value || typeof value !== "object") throw new Error("resource: \u9884\u53D6\u72B6\u6001\u683C\u5F0F\u65E0\u6548");
|
|
767
|
+
const candidate = value;
|
|
768
|
+
if (candidate.version !== 1 || !Array.isArray(candidate.entries)) {
|
|
769
|
+
throw new Error("resource: \u9884\u53D6\u72B6\u6001\u7248\u672C\u6216 entries \u65E0\u6548");
|
|
770
|
+
}
|
|
771
|
+
const entries = [];
|
|
772
|
+
for (const entry of candidate.entries) {
|
|
773
|
+
if (!entry || typeof entry !== "object") throw new Error("resource: \u9884\u53D6\u6761\u76EE\u683C\u5F0F\u65E0\u6548");
|
|
774
|
+
const candidateEntry = entry;
|
|
775
|
+
if (!Array.isArray(candidateEntry.key) || typeof candidateEntry.updatedAt !== "number" || !Number.isFinite(candidateEntry.updatedAt) || typeof candidateEntry.staleTime !== "number" || !Number.isFinite(candidateEntry.staleTime) || candidateEntry.staleTime < 0) {
|
|
776
|
+
throw new Error("resource: \u9884\u53D6\u6761\u76EE\u5B57\u6BB5\u65E0\u6548");
|
|
777
|
+
}
|
|
778
|
+
stableSerialize(candidateEntry.key);
|
|
779
|
+
entries.push({
|
|
780
|
+
key: candidateEntry.key,
|
|
781
|
+
data: candidateEntry.data,
|
|
782
|
+
updatedAt: candidateEntry.updatedAt,
|
|
783
|
+
staleTime: candidateEntry.staleTime
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
return { version: 1, entries };
|
|
787
|
+
}
|
|
788
|
+
__name(parseDehydratedState, "parseDehydratedState");
|
|
789
|
+
function serialize(value, stack) {
|
|
790
|
+
if (value === null) return "null";
|
|
791
|
+
switch (typeof value) {
|
|
792
|
+
case "string":
|
|
793
|
+
return `string:${JSON.stringify(value)}`;
|
|
794
|
+
case "boolean":
|
|
795
|
+
return `boolean:${value}`;
|
|
796
|
+
case "number":
|
|
797
|
+
if (Number.isNaN(value)) return "number:NaN";
|
|
798
|
+
if (Object.is(value, -0)) return "number:-0";
|
|
799
|
+
return `number:${value}`;
|
|
800
|
+
case "bigint":
|
|
801
|
+
return `bigint:${value}`;
|
|
802
|
+
case "undefined":
|
|
803
|
+
return "undefined";
|
|
804
|
+
case "function":
|
|
805
|
+
case "symbol":
|
|
806
|
+
throw new Error(`resource: key \u4E0D\u80FD\u5305\u542B ${typeof value}`);
|
|
807
|
+
}
|
|
808
|
+
const object = value;
|
|
809
|
+
if (stack.has(object)) throw new Error("resource: key \u4E0D\u80FD\u5305\u542B\u5FAA\u73AF\u5F15\u7528");
|
|
810
|
+
stack.add(object);
|
|
811
|
+
try {
|
|
812
|
+
if (Array.isArray(object)) {
|
|
813
|
+
return `array:[${object.map((item) => serialize(item, stack)).join(",")}]`;
|
|
814
|
+
}
|
|
815
|
+
if (object instanceof Date) return `date:${object.toJSON()}`;
|
|
816
|
+
if (object instanceof RegExp) return `regexp:${object.toString()}`;
|
|
817
|
+
const entries = Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${serialize(object[key], stack)}`);
|
|
818
|
+
return `object:{${entries.join(",")}}`;
|
|
819
|
+
} finally {
|
|
820
|
+
stack.delete(object);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
__name(serialize, "serialize");
|
|
824
|
+
|
|
825
|
+
export { createResourceClient, resource, serializeResourceState, stableSerialize };
|
|
826
|
+
//# sourceMappingURL=resource.js.map
|
|
827
|
+
//# sourceMappingURL=resource.js.map
|