sibujs 3.4.1 → 4.0.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/LICENSE +21 -21
- package/README.md +118 -15
- package/dist/browser.cjs +410 -101
- package/dist/browser.d.cts +50 -5
- package/dist/browser.d.ts +50 -5
- package/dist/browser.js +8 -6
- package/dist/build.cjs +784 -392
- package/dist/build.js +15 -15
- package/dist/cdn.global.js +11 -7
- package/dist/{chunk-PBZWJQNY.js → chunk-52ERIVFD.js} +8 -8
- package/dist/chunk-52XFPGSN.js +123 -0
- package/dist/{chunk-Y22QAKJX.js → chunk-5BVKQX2F.js} +4 -4
- package/dist/chunk-5INI7D2L.js +194 -0
- package/dist/{chunk-4I3M326N.js → chunk-AD5YSZAN.js} +19 -25
- package/dist/{chunk-F7HW2NI5.js → chunk-AW6HPJ3L.js} +1 -1
- package/dist/chunk-DHDZ7IVN.js +109 -0
- package/dist/{chunk-VQZDHAEK.js → chunk-E72G5JSK.js} +1 -1
- package/dist/{chunk-MPE75KOA.js → chunk-HPXTUPJD.js} +259 -124
- package/dist/{chunk-EX77FXTT.js → chunk-HYCCIYNS.js} +228 -51
- package/dist/{chunk-FWFEBYQ6.js → chunk-IKVUUE7D.js} +115 -41
- package/dist/{chunk-5MFF5EPT.js → chunk-J4WZMCIW.js} +5 -5
- package/dist/{chunk-BQLVTAUZ.js → chunk-JYXQCELN.js} +57 -22
- package/dist/{chunk-WEZ5SP7L.js → chunk-PHB3MCK4.js} +73 -34
- package/dist/chunk-PHRH66Y7.js +147 -0
- package/dist/{chunk-S2VW4NBA.js → chunk-PJDSI2K7.js} +295 -198
- package/dist/{chunk-QHPUFNHZ.js → chunk-PTLJ7NQX.js} +3 -3
- package/dist/{chunk-NXR2YFZB.js → chunk-QD4I7ZGZ.js} +24 -34
- package/dist/{chunk-S3NFJO6L.js → chunk-TIRZCERI.js} +36 -5
- package/dist/{chunk-7PNKW3FJ.js → chunk-TNEHT3NG.js} +232 -67
- package/dist/{chunk-335KJEN2.js → chunk-TVURNWDK.js} +5 -3
- package/dist/{chunk-CV7FAQ2R.js → chunk-UNAYBND2.js} +9 -3
- package/dist/chunk-VPP2FONR.js +142 -0
- package/dist/{chunk-X4JS367G.js → chunk-XDOMTQHM.js} +14 -10
- package/dist/chunk-XET2RUJF.js +61 -0
- package/dist/{chunk-3FFCUIGV.js → chunk-XZMCVZW6.js} +309 -188
- package/dist/{chunk-YDWP3ZOU.js → chunk-YLV26GAJ.js} +228 -86
- package/dist/{contracts-ey_Qh8ef.d.ts → contracts-Hg6ITAEF.d.cts} +6 -6
- package/dist/{contracts-ey_Qh8ef.d.cts → contracts-Hg6ITAEF.d.ts} +6 -6
- package/dist/{customElement-CPfIrbvg.d.cts → customElement-CKLoO63w.d.cts} +73 -11
- package/dist/{customElement-CPfIrbvg.d.ts → customElement-CKLoO63w.d.ts} +73 -11
- package/dist/data.cjs +483 -205
- package/dist/data.d.cts +139 -7
- package/dist/data.d.ts +139 -7
- package/dist/data.js +9 -9
- package/dist/devtools.cjs +222 -87
- package/dist/devtools.js +8 -8
- package/dist/dispose-4FZ6OFXR.js +19 -0
- package/dist/ecosystem.cjs +426 -182
- package/dist/ecosystem.js +11 -11
- package/dist/extras.cjs +1988 -772
- package/dist/extras.d.cts +7 -7
- package/dist/extras.d.ts +7 -7
- package/dist/extras.js +29 -26
- package/dist/index.cjs +791 -392
- package/dist/index.d.cts +299 -28
- package/dist/index.d.ts +299 -28
- package/dist/index.js +33 -18
- package/dist/motion.cjs +180 -50
- package/dist/motion.d.cts +26 -0
- package/dist/motion.d.ts +26 -0
- package/dist/motion.js +5 -5
- package/dist/patterns.cjs +437 -170
- package/dist/patterns.d.cts +60 -13
- package/dist/patterns.d.ts +60 -13
- package/dist/patterns.js +7 -7
- package/dist/performance.cjs +333 -148
- package/dist/performance.d.cts +55 -7
- package/dist/performance.d.ts +55 -7
- package/dist/performance.js +9 -9
- package/dist/plugins.cjs +1378 -583
- package/dist/plugins.d.cts +227 -9
- package/dist/plugins.d.ts +227 -9
- package/dist/plugins.js +610 -321
- package/dist/{ssr-D62yFwuw.d.cts → ssr-BiPRdZ6n.d.cts} +44 -15
- package/dist/{ssr-D62yFwuw.d.ts → ssr-BiPRdZ6n.d.ts} +44 -15
- package/dist/{ssr-2PPULEK2.js → ssr-JFP5535P.js} +5 -4
- package/dist/ssr.cjs +1111 -450
- package/dist/ssr.d.cts +230 -20
- package/dist/ssr.d.ts +230 -20
- package/dist/ssr.js +15 -12
- package/dist/{startup-0Qv6aosO.d.cts → startup-BMpaiMhP.d.cts} +1 -1
- package/dist/{startup-0Qv6aosO.d.ts → startup-BMpaiMhP.d.ts} +1 -1
- package/dist/testing.cjs +205 -63
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +8 -10
- package/dist/ui.cjs +640 -199
- package/dist/ui.d.cts +2 -2
- package/dist/ui.d.ts +2 -2
- package/dist/ui.js +10 -10
- package/dist/widgets.cjs +207 -82
- package/dist/widgets.js +9 -9
- package/package.json +12 -7
- package/dist/chunk-5VGSK6D2.js +0 -80
- package/dist/chunk-6CAJU5TP.js +0 -38
- package/dist/chunk-COY6PUD2.js +0 -34
- package/dist/chunk-DLBFEAIS.js +0 -96
- package/dist/chunk-L3GAGWCC.js +0 -111
|
@@ -1,25 +1,69 @@
|
|
|
1
1
|
import {
|
|
2
2
|
context
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-XET2RUJF.js";
|
|
4
4
|
import {
|
|
5
5
|
derived
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-AD5YSZAN.js";
|
|
7
|
+
import {
|
|
8
|
+
sanitizeUrl
|
|
9
|
+
} from "./chunk-5INI7D2L.js";
|
|
7
10
|
import {
|
|
8
11
|
globalSingleton
|
|
9
12
|
} from "./chunk-7ZHH77QA.js";
|
|
10
|
-
import {
|
|
11
|
-
sanitizeUrl
|
|
12
|
-
} from "./chunk-L3GAGWCC.js";
|
|
13
13
|
import {
|
|
14
14
|
effect
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-XDOMTQHM.js";
|
|
16
16
|
import {
|
|
17
17
|
getRequestScopedCache
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-TIRZCERI.js";
|
|
19
19
|
import {
|
|
20
20
|
batch,
|
|
21
21
|
signal
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-TVURNWDK.js";
|
|
23
|
+
|
|
24
|
+
// src/data/abort.ts
|
|
25
|
+
function isAbortError(error) {
|
|
26
|
+
return typeof error === "object" && error !== null && error.name === "AbortError";
|
|
27
|
+
}
|
|
28
|
+
function createAbortError(message = "Aborted") {
|
|
29
|
+
if (typeof DOMException !== "undefined") {
|
|
30
|
+
return new DOMException(message, "AbortError");
|
|
31
|
+
}
|
|
32
|
+
const error = new Error(message);
|
|
33
|
+
error.name = "AbortError";
|
|
34
|
+
return error;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/data/callbacks.ts
|
|
38
|
+
function reportCallbackError(label, error) {
|
|
39
|
+
if (typeof console !== "undefined") {
|
|
40
|
+
console.error(`[SibuJS data] ${label} threw. The operation's own status is unaffected.`, error);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function runCallback(label, fn) {
|
|
44
|
+
try {
|
|
45
|
+
fn();
|
|
46
|
+
} catch (error) {
|
|
47
|
+
reportCallbackError(label, error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function runSelect(label, fn) {
|
|
51
|
+
try {
|
|
52
|
+
return { ok: true, value: fn() };
|
|
53
|
+
} catch (error) {
|
|
54
|
+
reportCallbackError(label, error);
|
|
55
|
+
return { ok: false };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function notifyListeners(listeners, label) {
|
|
59
|
+
for (const listener of Array.from(listeners)) {
|
|
60
|
+
try {
|
|
61
|
+
listener();
|
|
62
|
+
} catch (error) {
|
|
63
|
+
reportCallbackError(label, error);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
23
67
|
|
|
24
68
|
// src/data/retry.ts
|
|
25
69
|
function calculateDelay(attempt, strategy, baseDelay, maxDelay, jitter) {
|
|
@@ -53,39 +97,54 @@ async function withRetry(fn, options, onRetry, signal2) {
|
|
|
53
97
|
const shouldRetry = options?.shouldRetry ?? (() => true);
|
|
54
98
|
let lastError;
|
|
55
99
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
56
|
-
if (signal2?.aborted) throw
|
|
100
|
+
if (signal2?.aborted) throw createAbortError();
|
|
57
101
|
try {
|
|
58
|
-
if (signal2?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
59
102
|
return await fn();
|
|
60
103
|
} catch (error) {
|
|
61
104
|
lastError = error;
|
|
105
|
+
if (signal2?.aborted) throw createAbortError();
|
|
106
|
+
if (isAbortError(error)) throw error;
|
|
62
107
|
if (attempt >= maxRetries || !shouldRetry(error, attempt)) throw error;
|
|
63
108
|
const delay = calculateDelay(attempt, strategy, baseDelay, maxDelay, jitter);
|
|
64
109
|
onRetry?.(error, attempt, delay);
|
|
65
|
-
await
|
|
66
|
-
let onAbort = null;
|
|
67
|
-
const timer = setTimeout(() => {
|
|
68
|
-
if (onAbort && signal2) signal2.removeEventListener("abort", onAbort);
|
|
69
|
-
resolve();
|
|
70
|
-
}, delay);
|
|
71
|
-
if (signal2) {
|
|
72
|
-
onAbort = () => {
|
|
73
|
-
clearTimeout(timer);
|
|
74
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
75
|
-
};
|
|
76
|
-
signal2.addEventListener("abort", onAbort, { once: true });
|
|
77
|
-
}
|
|
78
|
-
});
|
|
110
|
+
await waitForRetryDelay(delay, signal2);
|
|
79
111
|
}
|
|
80
112
|
}
|
|
81
113
|
throw lastError;
|
|
82
114
|
}
|
|
115
|
+
function waitForRetryDelay(delay, signal2) {
|
|
116
|
+
if (signal2?.aborted) return Promise.reject(createAbortError());
|
|
117
|
+
return new Promise((resolve, reject) => {
|
|
118
|
+
let settled = false;
|
|
119
|
+
const finish = (action) => {
|
|
120
|
+
if (settled) return;
|
|
121
|
+
settled = true;
|
|
122
|
+
clearTimeout(timer);
|
|
123
|
+
if (signal2) signal2.removeEventListener("abort", onAbort);
|
|
124
|
+
action();
|
|
125
|
+
};
|
|
126
|
+
const onAbort = () => finish(() => reject(createAbortError()));
|
|
127
|
+
const timer = setTimeout(() => finish(resolve), delay);
|
|
128
|
+
if (signal2) {
|
|
129
|
+
signal2.addEventListener("abort", onAbort, { once: true });
|
|
130
|
+
if (signal2.aborted) onAbort();
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
83
134
|
|
|
84
135
|
// src/data/query.ts
|
|
85
136
|
var globalQueryCache = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.query.cache.v1"), () => /* @__PURE__ */ new Map());
|
|
86
137
|
function getActiveQueryCache() {
|
|
87
138
|
return getRequestScopedCache("query") ?? globalQueryCache;
|
|
88
139
|
}
|
|
140
|
+
function abandonEntry(entry) {
|
|
141
|
+
if (entry.gcTimer) clearTimeout(entry.gcTimer);
|
|
142
|
+
entry.gcTimer = null;
|
|
143
|
+
entry.controller?.abort();
|
|
144
|
+
entry.controller = null;
|
|
145
|
+
entry.promise = null;
|
|
146
|
+
entry.generation++;
|
|
147
|
+
}
|
|
89
148
|
function getOrCreateEntry(cache, key, initialData) {
|
|
90
149
|
let entry = cache.get(key);
|
|
91
150
|
if (!entry) {
|
|
@@ -97,7 +156,9 @@ function getOrCreateEntry(cache, key, initialData) {
|
|
|
97
156
|
gcTimer: null,
|
|
98
157
|
promise: null,
|
|
99
158
|
listeners: /* @__PURE__ */ new Set(),
|
|
100
|
-
refetchers: /* @__PURE__ */ new Set()
|
|
159
|
+
refetchers: /* @__PURE__ */ new Set(),
|
|
160
|
+
controller: null,
|
|
161
|
+
generation: 0
|
|
101
162
|
};
|
|
102
163
|
cache.set(key, entry);
|
|
103
164
|
}
|
|
@@ -123,9 +184,10 @@ function query(key, fetcher, options = {}) {
|
|
|
123
184
|
const [data, setData] = signal(initialData);
|
|
124
185
|
const [isFetching, setIsFetching] = signal(false);
|
|
125
186
|
const [error, setError] = signal(void 0);
|
|
126
|
-
let abortController = null;
|
|
127
187
|
let disposed = false;
|
|
128
188
|
let currentKey = null;
|
|
189
|
+
let attachedEntry = null;
|
|
190
|
+
let attachedKey = null;
|
|
129
191
|
let intervalTimer = null;
|
|
130
192
|
const loading = derived(() => isFetching() && data() === void 0);
|
|
131
193
|
const isStale = derived(() => {
|
|
@@ -135,82 +197,128 @@ function query(key, fetcher, options = {}) {
|
|
|
135
197
|
if (!entry || entry.dataUpdatedAt === 0) return true;
|
|
136
198
|
return Date.now() - entry.dataUpdatedAt >= staleTime;
|
|
137
199
|
});
|
|
200
|
+
function detachFromEntry() {
|
|
201
|
+
const entry = attachedEntry;
|
|
202
|
+
const key2 = attachedKey;
|
|
203
|
+
if (!entry) return;
|
|
204
|
+
attachedEntry = null;
|
|
205
|
+
attachedKey = null;
|
|
206
|
+
entry.listeners.delete(onCacheUpdate);
|
|
207
|
+
entry.refetchers.delete(doFetch);
|
|
208
|
+
entry.subscribers = Math.max(0, entry.subscribers - 1);
|
|
209
|
+
if (entry.subscribers > 0 || cacheTime < 0 || key2 === null) return;
|
|
210
|
+
if (cache.get(key2) !== entry) return;
|
|
211
|
+
if (entry.gcTimer !== null) clearTimeout(entry.gcTimer);
|
|
212
|
+
entry.gcTimer = setTimeout(() => {
|
|
213
|
+
const current = cache.get(key2);
|
|
214
|
+
if (current === entry && current.subscribers <= 0) {
|
|
215
|
+
abandonEntry(current);
|
|
216
|
+
cache.delete(key2);
|
|
217
|
+
}
|
|
218
|
+
}, cacheTime);
|
|
219
|
+
entry.gcTimer.unref?.();
|
|
220
|
+
}
|
|
221
|
+
function attachToEntry(entry, key2) {
|
|
222
|
+
if (attachedEntry === entry) {
|
|
223
|
+
if (entry.gcTimer !== null) {
|
|
224
|
+
clearTimeout(entry.gcTimer);
|
|
225
|
+
entry.gcTimer = null;
|
|
226
|
+
}
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
detachFromEntry();
|
|
230
|
+
attachedEntry = entry;
|
|
231
|
+
attachedKey = key2;
|
|
232
|
+
entry.subscribers++;
|
|
233
|
+
entry.listeners.add(onCacheUpdate);
|
|
234
|
+
entry.refetchers.add(doFetch);
|
|
235
|
+
if (entry.gcTimer !== null) {
|
|
236
|
+
clearTimeout(entry.gcTimer);
|
|
237
|
+
entry.gcTimer = null;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
138
240
|
async function doFetch() {
|
|
139
241
|
if (disposed || !currentKey || !enabled) return;
|
|
140
242
|
const key2 = currentKey;
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
entry = getOrCreateEntry(cache, key2);
|
|
144
|
-
entry.listeners.add(onCacheUpdate);
|
|
145
|
-
entry.refetchers.add(doFetch);
|
|
146
|
-
}
|
|
243
|
+
const entry = getOrCreateEntry(cache, key2);
|
|
244
|
+
attachToEntry(entry, key2);
|
|
147
245
|
if (entry.promise) {
|
|
148
246
|
setIsFetching(true);
|
|
149
247
|
const captured = entry.promise;
|
|
150
248
|
try {
|
|
151
249
|
await captured;
|
|
152
|
-
if (disposed || currentKey !== key2) return;
|
|
153
|
-
if (entry.promise === captured) {
|
|
154
|
-
onCacheUpdate();
|
|
155
|
-
if (entry.error) onError?.(entry.error);
|
|
156
|
-
else if (entry.data !== void 0) onSuccess?.(entry.data);
|
|
157
|
-
}
|
|
158
250
|
} catch {
|
|
159
|
-
|
|
160
|
-
if (
|
|
251
|
+
} finally {
|
|
252
|
+
if (!disposed && currentKey === key2) {
|
|
161
253
|
onCacheUpdate();
|
|
162
|
-
if (entry.error)
|
|
254
|
+
if (entry.error) {
|
|
255
|
+
const settledError = entry.error;
|
|
256
|
+
runCallback("query onError", () => onError?.(settledError));
|
|
257
|
+
} else if (entry.data !== void 0) {
|
|
258
|
+
const settledData = entry.data;
|
|
259
|
+
runCallback("query onSuccess", () => onSuccess?.(settledData));
|
|
260
|
+
}
|
|
261
|
+
runCallback("query onSettled", () => onSettled?.());
|
|
163
262
|
}
|
|
164
|
-
} finally {
|
|
165
|
-
if (!disposed && currentKey === key2) onSettled?.();
|
|
166
263
|
}
|
|
167
264
|
return;
|
|
168
265
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const
|
|
266
|
+
entry.controller = new AbortController();
|
|
267
|
+
const signal2 = entry.controller.signal;
|
|
268
|
+
const generation = ++entry.generation;
|
|
172
269
|
setIsFetching(true);
|
|
173
270
|
let promise;
|
|
174
271
|
try {
|
|
175
272
|
promise = withRetry(() => fetcher({ signal: signal2, key: key2 }), retryOptions, void 0, signal2);
|
|
176
273
|
} catch (err) {
|
|
177
274
|
setIsFetching(false);
|
|
275
|
+
if (isAbortError(err)) return;
|
|
178
276
|
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
179
277
|
entry.error = errorObj;
|
|
180
|
-
onError?.(errorObj);
|
|
181
|
-
onSettled?.();
|
|
278
|
+
runCallback("query onError", () => onError?.(errorObj));
|
|
279
|
+
runCallback("query onSettled", () => onSettled?.());
|
|
182
280
|
return;
|
|
183
281
|
}
|
|
184
282
|
entry.promise = promise;
|
|
185
283
|
try {
|
|
186
284
|
const result = await promise;
|
|
285
|
+
if (entry.generation !== generation) return;
|
|
187
286
|
entry.promise = null;
|
|
188
|
-
|
|
287
|
+
entry.controller = null;
|
|
189
288
|
entry.data = result;
|
|
190
289
|
entry.dataUpdatedAt = Date.now();
|
|
191
290
|
entry.error = void 0;
|
|
192
|
-
|
|
291
|
+
notifyListeners(entry.listeners, "query cache listener");
|
|
292
|
+
if (disposed || currentKey !== key2) return;
|
|
293
|
+
const selected = runSelect("query select", () => select ? select(result) : result);
|
|
193
294
|
batch(() => {
|
|
194
|
-
setData(selected);
|
|
295
|
+
if (selected.ok) setData(selected.value);
|
|
195
296
|
setIsFetching(false);
|
|
196
297
|
setError(void 0);
|
|
197
298
|
});
|
|
198
|
-
|
|
199
|
-
onSuccess?.(result);
|
|
299
|
+
runCallback("query onSuccess", () => onSuccess?.(result));
|
|
200
300
|
} catch (err) {
|
|
301
|
+
if (entry.generation !== generation) return;
|
|
201
302
|
entry.promise = null;
|
|
202
|
-
|
|
203
|
-
if (err
|
|
303
|
+
entry.controller = null;
|
|
304
|
+
if (isAbortError(err)) {
|
|
305
|
+
notifyListeners(entry.listeners, "query cache listener");
|
|
306
|
+
if (!disposed && currentKey === key2) setIsFetching(false);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
204
309
|
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
205
310
|
entry.error = errorObj;
|
|
311
|
+
notifyListeners(entry.listeners, "query cache listener");
|
|
312
|
+
if (disposed || currentKey !== key2) return;
|
|
206
313
|
batch(() => {
|
|
207
314
|
setError(errorObj);
|
|
208
315
|
setIsFetching(false);
|
|
209
316
|
});
|
|
210
|
-
|
|
211
|
-
onError?.(errorObj);
|
|
317
|
+
runCallback("query onError", () => onError?.(errorObj));
|
|
212
318
|
} finally {
|
|
213
|
-
if (!disposed && currentKey === key2)
|
|
319
|
+
if (!disposed && currentKey === key2 && entry.generation === generation) {
|
|
320
|
+
runCallback("query onSettled", () => onSettled?.());
|
|
321
|
+
}
|
|
214
322
|
}
|
|
215
323
|
}
|
|
216
324
|
function onCacheUpdate() {
|
|
@@ -225,43 +333,24 @@ function query(key, fetcher, options = {}) {
|
|
|
225
333
|
return;
|
|
226
334
|
}
|
|
227
335
|
const raw = entry.data;
|
|
228
|
-
const selected = raw !== void 0 && select ? select(raw) : raw;
|
|
336
|
+
const selected = raw !== void 0 && select ? runSelect("query select", () => select(raw)) : { ok: true, value: raw };
|
|
229
337
|
batch(() => {
|
|
230
|
-
setData(selected);
|
|
338
|
+
if (selected.ok) setData(selected.value);
|
|
231
339
|
setError(entry.error);
|
|
232
340
|
if (!entry.promise) setIsFetching(false);
|
|
233
341
|
});
|
|
234
342
|
}
|
|
235
343
|
const effectCleanup = effect(() => {
|
|
236
344
|
const key2 = resolveKey();
|
|
237
|
-
if (currentKey !== null && currentKey !== key2) {
|
|
238
|
-
const oldEntry = cache.get(currentKey);
|
|
239
|
-
if (oldEntry) {
|
|
240
|
-
oldEntry.listeners.delete(onCacheUpdate);
|
|
241
|
-
oldEntry.refetchers.delete(doFetch);
|
|
242
|
-
oldEntry.subscribers--;
|
|
243
|
-
if (oldEntry.subscribers <= 0 && cacheTime >= 0) {
|
|
244
|
-
const oldKey = currentKey;
|
|
245
|
-
if (oldEntry.gcTimer !== null) clearTimeout(oldEntry.gcTimer);
|
|
246
|
-
oldEntry.gcTimer = setTimeout(() => cache.delete(oldKey), cacheTime);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
345
|
const keyChanged = currentKey !== key2;
|
|
251
346
|
currentKey = key2;
|
|
252
347
|
const entry = getOrCreateEntry(cache, key2, initialData);
|
|
253
|
-
|
|
254
|
-
if (entry.gcTimer !== null) {
|
|
255
|
-
clearTimeout(entry.gcTimer);
|
|
256
|
-
entry.gcTimer = null;
|
|
257
|
-
}
|
|
258
|
-
entry.listeners.add(onCacheUpdate);
|
|
259
|
-
entry.refetchers.add(doFetch);
|
|
348
|
+
attachToEntry(entry, key2);
|
|
260
349
|
if (entry.data !== void 0) {
|
|
261
350
|
const raw = entry.data;
|
|
262
|
-
const selected = select ? select(raw) : raw;
|
|
351
|
+
const selected = runSelect("query select", () => select ? select(raw) : raw);
|
|
263
352
|
batch(() => {
|
|
264
|
-
setData(selected);
|
|
353
|
+
if (selected.ok) setData(selected.value);
|
|
265
354
|
setError(entry.error);
|
|
266
355
|
});
|
|
267
356
|
}
|
|
@@ -299,22 +388,9 @@ function query(key, fetcher, options = {}) {
|
|
|
299
388
|
function dispose() {
|
|
300
389
|
if (disposed) return;
|
|
301
390
|
disposed = true;
|
|
302
|
-
abortController?.abort();
|
|
303
391
|
effectCleanup();
|
|
304
392
|
if (intervalTimer) clearInterval(intervalTimer);
|
|
305
|
-
|
|
306
|
-
const entry = cache.get(currentKey);
|
|
307
|
-
if (entry) {
|
|
308
|
-
entry.listeners.delete(onCacheUpdate);
|
|
309
|
-
entry.refetchers.delete(doFetch);
|
|
310
|
-
entry.subscribers--;
|
|
311
|
-
if (entry.subscribers <= 0 && cacheTime >= 0) {
|
|
312
|
-
const key2 = currentKey;
|
|
313
|
-
if (entry.gcTimer !== null) clearTimeout(entry.gcTimer);
|
|
314
|
-
entry.gcTimer = setTimeout(() => cache.delete(key2), cacheTime);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
}
|
|
393
|
+
detachFromEntry();
|
|
318
394
|
if (focusHandler && typeof globalThis.removeEventListener === "function") {
|
|
319
395
|
globalThis.removeEventListener("focus", focusHandler);
|
|
320
396
|
}
|
|
@@ -350,21 +426,21 @@ function setQueryData(key, data) {
|
|
|
350
426
|
const newData = typeof data === "function" ? data(entry.data) : data;
|
|
351
427
|
entry.data = newData;
|
|
352
428
|
entry.dataUpdatedAt = Date.now();
|
|
353
|
-
|
|
429
|
+
notifyListeners(entry.listeners, "query cache listener");
|
|
354
430
|
}
|
|
355
431
|
function clearQueryCache() {
|
|
356
432
|
const activeListeners = [];
|
|
357
433
|
const activeRefetchers = [];
|
|
358
434
|
const activeCache = getActiveQueryCache();
|
|
359
435
|
for (const entry of activeCache.values()) {
|
|
360
|
-
if (entry.gcTimer) clearTimeout(entry.gcTimer);
|
|
361
436
|
if (entry.subscribers > 0) {
|
|
362
437
|
for (const listener of entry.listeners) activeListeners.push(listener);
|
|
363
438
|
for (const refetcher of entry.refetchers) activeRefetchers.push(refetcher);
|
|
364
439
|
}
|
|
440
|
+
abandonEntry(entry);
|
|
365
441
|
}
|
|
366
442
|
activeCache.clear();
|
|
367
|
-
|
|
443
|
+
notifyListeners(activeListeners, "query cache listener");
|
|
368
444
|
for (const refetcher of activeRefetchers) {
|
|
369
445
|
refetcher().catch((err) => {
|
|
370
446
|
if (typeof console !== "undefined") {
|
|
@@ -376,7 +452,7 @@ function clearQueryCache() {
|
|
|
376
452
|
function __resetQueryCache() {
|
|
377
453
|
const activeCache = getActiveQueryCache();
|
|
378
454
|
for (const entry of activeCache.values()) {
|
|
379
|
-
|
|
455
|
+
abandonEntry(entry);
|
|
380
456
|
}
|
|
381
457
|
activeCache.clear();
|
|
382
458
|
}
|
|
@@ -391,6 +467,8 @@ function mutation(mutationFn, options = {}) {
|
|
|
391
467
|
const isIdle = derived(() => status() === "idle");
|
|
392
468
|
let runId = 0;
|
|
393
469
|
let abortController = null;
|
|
470
|
+
let terminalStatus = "idle";
|
|
471
|
+
let terminalError;
|
|
394
472
|
async function execute(variables) {
|
|
395
473
|
abortController?.abort();
|
|
396
474
|
abortController = new AbortController();
|
|
@@ -408,25 +486,38 @@ function mutation(mutationFn, options = {}) {
|
|
|
408
486
|
}
|
|
409
487
|
const result = await withRetry(() => mutationFn(variables, signal2), options.retry, void 0, signal2);
|
|
410
488
|
if (myRun !== runId) return result;
|
|
489
|
+
terminalStatus = "success";
|
|
490
|
+
terminalError = void 0;
|
|
411
491
|
batch(() => {
|
|
412
492
|
setData(result);
|
|
413
493
|
setLoading(false);
|
|
414
494
|
setStatus("success");
|
|
415
495
|
});
|
|
416
|
-
options.onSuccess?.(result, variables, context2);
|
|
417
|
-
options.onSettled?.(result, void 0, variables, context2);
|
|
496
|
+
runCallback("mutation onSuccess", () => options.onSuccess?.(result, variables, context2));
|
|
497
|
+
runCallback("mutation onSettled", () => options.onSettled?.(result, void 0, variables, context2));
|
|
418
498
|
return result;
|
|
419
499
|
} catch (err) {
|
|
500
|
+
if (isAbortError(err)) {
|
|
501
|
+
if (myRun === runId) {
|
|
502
|
+
batch(() => {
|
|
503
|
+
setLoading(false);
|
|
504
|
+
setError(terminalError);
|
|
505
|
+
setStatus(terminalStatus);
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
throw err;
|
|
509
|
+
}
|
|
420
510
|
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
421
|
-
if (errorObj instanceof DOMException && errorObj.name === "AbortError") throw errorObj;
|
|
422
511
|
if (myRun !== runId) throw errorObj;
|
|
512
|
+
terminalStatus = "error";
|
|
513
|
+
terminalError = errorObj;
|
|
423
514
|
batch(() => {
|
|
424
515
|
setError(errorObj);
|
|
425
516
|
setLoading(false);
|
|
426
517
|
setStatus("error");
|
|
427
518
|
});
|
|
428
|
-
options.onError?.(errorObj, variables, context2);
|
|
429
|
-
options.onSettled?.(void 0, errorObj, variables, context2);
|
|
519
|
+
runCallback("mutation onError", () => options.onError?.(errorObj, variables, context2));
|
|
520
|
+
runCallback("mutation onSettled", () => options.onSettled?.(void 0, errorObj, variables, context2));
|
|
430
521
|
throw errorObj;
|
|
431
522
|
}
|
|
432
523
|
}
|
|
@@ -434,6 +525,8 @@ function mutation(mutationFn, options = {}) {
|
|
|
434
525
|
runId++;
|
|
435
526
|
abortController?.abort();
|
|
436
527
|
abortController = null;
|
|
528
|
+
terminalStatus = "idle";
|
|
529
|
+
terminalError = void 0;
|
|
437
530
|
batch(() => {
|
|
438
531
|
setData(void 0);
|
|
439
532
|
setError(void 0);
|
|
@@ -449,7 +542,7 @@ function mutation(mutationFn, options = {}) {
|
|
|
449
542
|
isIdle,
|
|
450
543
|
mutate: (variables) => {
|
|
451
544
|
execute(variables).catch((err) => {
|
|
452
|
-
if (err
|
|
545
|
+
if (isAbortError(err)) return;
|
|
453
546
|
if (typeof console !== "undefined") {
|
|
454
547
|
console.warn("[SibuJS mutation] mutate() failed; check `.error()` signal or onError option.", err);
|
|
455
548
|
}
|
|
@@ -522,10 +615,17 @@ function infiniteQuery(key, fetcher, options) {
|
|
|
522
615
|
setIsFetchingNext(false);
|
|
523
616
|
setIsFetchingPrev(false);
|
|
524
617
|
});
|
|
525
|
-
onSuccess?.(newPages);
|
|
618
|
+
runCallback("infiniteQuery onSuccess", () => onSuccess?.(newPages));
|
|
526
619
|
} catch (err) {
|
|
527
620
|
if (disposed || myRun !== runId) return;
|
|
528
|
-
if (err
|
|
621
|
+
if (isAbortError(err)) {
|
|
622
|
+
batch(() => {
|
|
623
|
+
setIsFetching(false);
|
|
624
|
+
setIsFetchingNext(false);
|
|
625
|
+
setIsFetchingPrev(false);
|
|
626
|
+
});
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
529
629
|
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
530
630
|
batch(() => {
|
|
531
631
|
setError(errorObj);
|
|
@@ -533,7 +633,7 @@ function infiniteQuery(key, fetcher, options) {
|
|
|
533
633
|
setIsFetchingNext(false);
|
|
534
634
|
setIsFetchingPrev(false);
|
|
535
635
|
});
|
|
536
|
-
onError?.(errorObj);
|
|
636
|
+
runCallback("infiniteQuery onError", () => onError?.(errorObj));
|
|
537
637
|
}
|
|
538
638
|
})();
|
|
539
639
|
inFlight = promise;
|
|
@@ -715,7 +815,7 @@ function resource(sourceOrFetcher, fetcherOrOptions, maybeOptions) {
|
|
|
715
815
|
setLoading(true);
|
|
716
816
|
setError(void 0);
|
|
717
817
|
});
|
|
718
|
-
options.onStart?.();
|
|
818
|
+
runCallback("resource onStart", () => options.onStart?.());
|
|
719
819
|
try {
|
|
720
820
|
const result = await withRetry(() => fetcher(sourceValue, { signal: signal2, prev }), options.retry, void 0, signal2);
|
|
721
821
|
if (version !== fetchVersion || disposed) return;
|
|
@@ -724,10 +824,10 @@ function resource(sourceOrFetcher, fetcherOrOptions, maybeOptions) {
|
|
|
724
824
|
setData(result);
|
|
725
825
|
setLoading(false);
|
|
726
826
|
});
|
|
727
|
-
options.onSuccess?.(result);
|
|
827
|
+
runCallback("resource onSuccess", () => options.onSuccess?.(result));
|
|
728
828
|
} catch (err) {
|
|
729
829
|
if (version !== fetchVersion || disposed) return;
|
|
730
|
-
if (err
|
|
830
|
+
if (isAbortError(err)) {
|
|
731
831
|
if (version === fetchVersion) setLoading(false);
|
|
732
832
|
return;
|
|
733
833
|
}
|
|
@@ -736,10 +836,10 @@ function resource(sourceOrFetcher, fetcherOrOptions, maybeOptions) {
|
|
|
736
836
|
setError(errorObj);
|
|
737
837
|
setLoading(false);
|
|
738
838
|
});
|
|
739
|
-
options.onError?.(errorObj);
|
|
839
|
+
runCallback("resource onError", () => options.onError?.(errorObj));
|
|
740
840
|
} finally {
|
|
741
841
|
if (version === fetchVersion) {
|
|
742
|
-
options.onSettled?.();
|
|
842
|
+
runCallback("resource onSettled", () => options.onSettled?.());
|
|
743
843
|
}
|
|
744
844
|
}
|
|
745
845
|
}
|
|
@@ -927,13 +1027,14 @@ async function offlineStore(options) {
|
|
|
927
1027
|
return data().filter(filter);
|
|
928
1028
|
}
|
|
929
1029
|
async function sync() {
|
|
930
|
-
|
|
1030
|
+
const syncAdapter2 = adapter;
|
|
1031
|
+
if (!syncAdapter2 || isSyncing() || closed) return;
|
|
931
1032
|
setIsSyncing(true);
|
|
932
1033
|
try {
|
|
933
1034
|
const snapshot = await idbGetAllWithKeys(db, "_changes");
|
|
934
1035
|
if (closed) return;
|
|
935
1036
|
if (snapshot.length > 0) {
|
|
936
|
-
const result = await
|
|
1037
|
+
const result = await syncAdapter2.push(snapshot.map((e) => e.value));
|
|
937
1038
|
if (closed) return;
|
|
938
1039
|
if (result.ok) {
|
|
939
1040
|
await idbDeleteKeys(
|
|
@@ -946,20 +1047,18 @@ async function offlineStore(options) {
|
|
|
946
1047
|
console.warn(`[offlineStore] push rejected by adapter${result.error ? `: ${result.error}` : ""}`);
|
|
947
1048
|
}
|
|
948
1049
|
}
|
|
949
|
-
const remoteItems = await
|
|
1050
|
+
const remoteItems = await syncAdapter2.pull(lastSynced());
|
|
950
1051
|
if (closed) return;
|
|
951
1052
|
const pendingChanges = await idbGetAll(db, "_changes");
|
|
952
1053
|
if (closed) return;
|
|
953
|
-
const
|
|
1054
|
+
const pendingByKey = /* @__PURE__ */ new Map();
|
|
954
1055
|
for (const c of pendingChanges) {
|
|
955
1056
|
const k = c.item[keyPath];
|
|
956
|
-
if (k != null)
|
|
1057
|
+
if (k != null) pendingByKey.set(k, c);
|
|
957
1058
|
}
|
|
958
|
-
const
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
});
|
|
962
|
-
await idbPutMany(db, "items", safeRemote);
|
|
1059
|
+
const resolved = await resolveRemoteItems(syncAdapter2, remoteItems, pendingByKey);
|
|
1060
|
+
if (closed) return;
|
|
1061
|
+
await idbPutMany(db, "items", resolved);
|
|
963
1062
|
if (closed) return;
|
|
964
1063
|
const now = Date.now();
|
|
965
1064
|
await idbPut(db, "_meta", now, "lastSynced");
|
|
@@ -972,6 +1071,42 @@ async function offlineStore(options) {
|
|
|
972
1071
|
setIsSyncing(false);
|
|
973
1072
|
}
|
|
974
1073
|
}
|
|
1074
|
+
async function resolveRemoteItems(activeAdapter, remoteItems, pendingByKey) {
|
|
1075
|
+
const strategy = activeAdapter.conflictStrategy ?? "client-wins";
|
|
1076
|
+
const out = [];
|
|
1077
|
+
for (const remote of remoteItems) {
|
|
1078
|
+
const key = remote[keyPath];
|
|
1079
|
+
const pending = key == null ? void 0 : pendingByKey.get(key);
|
|
1080
|
+
if (!pending) {
|
|
1081
|
+
out.push(remote);
|
|
1082
|
+
continue;
|
|
1083
|
+
}
|
|
1084
|
+
if (strategy === "server-wins") {
|
|
1085
|
+
out.push(remote);
|
|
1086
|
+
continue;
|
|
1087
|
+
}
|
|
1088
|
+
if (strategy === "manual") {
|
|
1089
|
+
if (!activeAdapter.resolveConflict) {
|
|
1090
|
+
if (typeof console !== "undefined") {
|
|
1091
|
+
console.warn(
|
|
1092
|
+
'[offlineStore] conflictStrategy is "manual" but the adapter has no resolveConflict(); keeping the local record for this key (client-wins).'
|
|
1093
|
+
);
|
|
1094
|
+
}
|
|
1095
|
+
continue;
|
|
1096
|
+
}
|
|
1097
|
+
try {
|
|
1098
|
+
const local = pending.type === "delete" ? void 0 : await idbGet(db, "items", key);
|
|
1099
|
+
const choice = await activeAdapter.resolveConflict({ key, local, remote, pending });
|
|
1100
|
+
if (choice !== void 0) out.push(choice);
|
|
1101
|
+
} catch (err) {
|
|
1102
|
+
if (typeof console !== "undefined") {
|
|
1103
|
+
console.warn("[offlineStore] resolveConflict threw; keeping the local record", err);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
return out;
|
|
1109
|
+
}
|
|
975
1110
|
function attach(newAdapter) {
|
|
976
1111
|
adapter = newAdapter;
|
|
977
1112
|
}
|