sibujs 3.4.0 → 4.0.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/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 +777 -385
- package/dist/build.js +15 -15
- package/dist/cdn.global.js +11 -7
- package/dist/{chunk-JCO7AHNU.js → chunk-3EKJK5FZ.js} +73 -34
- package/dist/chunk-4MFZMLUU.js +147 -0
- package/dist/chunk-52XFPGSN.js +123 -0
- package/dist/chunk-5INI7D2L.js +194 -0
- package/dist/{chunk-OAVXAFAY.js → chunk-6LG2T7LO.js} +5 -5
- package/dist/{chunk-OOUQUCNN.js → chunk-ACBT6UZS.js} +57 -22
- package/dist/{chunk-F7HW2NI5.js → chunk-AW6HPJ3L.js} +1 -1
- package/dist/chunk-DHDZ7IVN.js +109 -0
- package/dist/{chunk-JNGGM2XE.js → chunk-DQVUBVNX.js} +16 -25
- package/dist/{chunk-AXFWGY32.js → chunk-DR7H6UGM.js} +232 -67
- package/dist/{chunk-WSGLJYFI.js → chunk-E62UADKL.js} +9 -3
- package/dist/{chunk-3Z2NGWQI.js → chunk-ES2GERY2.js} +1 -1
- package/dist/{chunk-S3V6ZXPI.js → chunk-EWEE7NBF.js} +115 -41
- package/dist/{chunk-3DVGCW7O.js → chunk-F64ZPCFE.js} +8 -8
- package/dist/{chunk-APFRCIRI.js → chunk-GCSGGA3Q.js} +295 -198
- package/dist/{chunk-EX77FXTT.js → chunk-HYCCIYNS.js} +228 -51
- package/dist/{chunk-ELXMRSVN.js → chunk-NF3LYCQH.js} +259 -124
- package/dist/chunk-O3QQ3XOL.js +61 -0
- package/dist/{chunk-BUKYCWHX.js → chunk-PBQEW5VW.js} +14 -10
- package/dist/{chunk-H5FI5WDP.js → chunk-RRIDJQ5C.js} +3 -3
- package/dist/{chunk-S3NFJO6L.js → chunk-TIRZCERI.js} +36 -5
- package/dist/{chunk-ODXVQ3CS.js → chunk-VFHIVEPF.js} +309 -188
- package/dist/chunk-VPP2FONR.js +142 -0
- package/dist/{chunk-KZBEVFXZ.js → chunk-W55YHPEP.js} +5 -3
- package/dist/{chunk-FKQG7FTY.js → chunk-WN6WDFCB.js} +4 -4
- package/dist/{chunk-CEBWDD63.js → chunk-WOOMUJFT.js} +228 -86
- package/dist/{chunk-ITWCTZYH.js → chunk-Z2CE42DB.js} +19 -25
- package/dist/{contracts-ey_Qh8ef.d.cts → contracts-Hg6ITAEF.d.cts} +6 -6
- package/dist/{contracts-ey_Qh8ef.d.ts → contracts-Hg6ITAEF.d.ts} +6 -6
- package/dist/{customElement-CPfIrbvg.d.ts → customElement-CKLoO63w.d.cts} +73 -11
- package/dist/{customElement-CPfIrbvg.d.cts → 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 +419 -175
- package/dist/ecosystem.js +11 -11
- package/dist/extras.cjs +1981 -765
- package/dist/extras.d.cts +7 -7
- package/dist/extras.d.ts +7 -7
- package/dist/extras.js +29 -26
- package/dist/index.cjs +784 -385
- 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 +1374 -576
- package/dist/plugins.d.cts +227 -9
- package/dist/plugins.d.ts +227 -9
- package/dist/plugins.js +613 -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 +1104 -443
- 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-COY6PUD2.js +0 -34
- package/dist/chunk-K35BAR4P.js +0 -96
- package/dist/chunk-L3GAGWCC.js +0 -111
- package/dist/chunk-RT3IYFN2.js +0 -38
package/dist/data.cjs
CHANGED
|
@@ -66,8 +66,95 @@ function devWarn(message) {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
// src/core/errors.ts
|
|
70
|
+
var ERROR_STATE_KEY = /* @__PURE__ */ Symbol.for("sibujs.runtime-errors.v1");
|
|
71
|
+
function state() {
|
|
72
|
+
const g = globalThis;
|
|
73
|
+
const existing = g[ERROR_STATE_KEY];
|
|
74
|
+
if (existing) return existing;
|
|
75
|
+
const created = { handler: null, handlerDepth: 0 };
|
|
76
|
+
g[ERROR_STATE_KEY] = created;
|
|
77
|
+
return created;
|
|
78
|
+
}
|
|
79
|
+
function normalizeError(value) {
|
|
80
|
+
if (value instanceof Error) return value;
|
|
81
|
+
const message = typeof value === "string" ? value : `Non-Error value thrown: ${safeStringify(value)}`;
|
|
82
|
+
return new Error(message, { cause: value });
|
|
83
|
+
}
|
|
84
|
+
function safeStringify(value) {
|
|
85
|
+
if (typeof value === "symbol") return value.toString();
|
|
86
|
+
try {
|
|
87
|
+
return typeof value === "object" && value !== null ? JSON.stringify(value) : String(value);
|
|
88
|
+
} catch {
|
|
89
|
+
return Object.prototype.toString.call(value);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function describe(context2) {
|
|
93
|
+
return context2.name ? `${context2.phase} "${context2.name}"` : context2.phase;
|
|
94
|
+
}
|
|
95
|
+
var REACTIVE_REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
|
|
96
|
+
function untrackedRun(fn) {
|
|
97
|
+
const api = globalThis[REACTIVE_REGISTRY_KEY];
|
|
98
|
+
return api?.untracked ? api.untracked(fn) : fn();
|
|
99
|
+
}
|
|
100
|
+
var PROPAGATE_EVENT = "sibu:error-propagate";
|
|
101
|
+
function claimedByBoundary(error, context2) {
|
|
102
|
+
const raw = context2.node;
|
|
103
|
+
if (!raw || typeof CustomEvent === "undefined") return false;
|
|
104
|
+
const target = resolveEventTarget(raw);
|
|
105
|
+
if (!target) return false;
|
|
106
|
+
try {
|
|
107
|
+
const event = new CustomEvent(PROPAGATE_EVENT, {
|
|
108
|
+
bubbles: true,
|
|
109
|
+
cancelable: true,
|
|
110
|
+
detail: { error, context: context2 }
|
|
111
|
+
});
|
|
112
|
+
return target.dispatchEvent(event) === false;
|
|
113
|
+
} catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function resolveEventTarget(node) {
|
|
118
|
+
const ELEMENT_NODE = 1;
|
|
119
|
+
let current = node;
|
|
120
|
+
while (current) {
|
|
121
|
+
if (current.nodeType === ELEMENT_NODE && typeof current.dispatchEvent === "function") {
|
|
122
|
+
return current;
|
|
123
|
+
}
|
|
124
|
+
current = current.parentNode;
|
|
125
|
+
}
|
|
126
|
+
const self = node;
|
|
127
|
+
return typeof self.dispatchEvent === "function" ? self : null;
|
|
128
|
+
}
|
|
129
|
+
function defaultReport(error, context2) {
|
|
130
|
+
if (typeof console === "undefined") return;
|
|
131
|
+
console.error(`[SibuJS] Uncaught error during ${describe(context2)}: ${error.message}`, error);
|
|
132
|
+
}
|
|
133
|
+
function reportError(error, context2) {
|
|
134
|
+
const err = normalizeError(error);
|
|
135
|
+
untrackedRun(() => {
|
|
136
|
+
if (claimedByBoundary(err, context2)) return;
|
|
137
|
+
const s = state();
|
|
138
|
+
const handler = s.handler;
|
|
139
|
+
if (handler && s.handlerDepth === 0) {
|
|
140
|
+
s.handlerDepth++;
|
|
141
|
+
try {
|
|
142
|
+
handler(error instanceof Error ? error : err, context2);
|
|
143
|
+
return;
|
|
144
|
+
} catch (handlerError) {
|
|
145
|
+
if (typeof console !== "undefined") {
|
|
146
|
+
const detail = handlerError instanceof Error ? handlerError.message : String(handlerError);
|
|
147
|
+
console.error(`[SibuJS] runtime error handler itself threw: ${detail}`, handlerError);
|
|
148
|
+
}
|
|
149
|
+
} finally {
|
|
150
|
+
s.handlerDepth--;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
defaultReport(err, context2);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
69
157
|
// src/reactivity/track-core.ts
|
|
70
|
-
var _isDev2 = isDev();
|
|
71
158
|
var POOL_MAX = 4096;
|
|
72
159
|
var nodePool = [];
|
|
73
160
|
function createNode() {
|
|
@@ -75,6 +162,7 @@ function createNode() {
|
|
|
75
162
|
sig: null,
|
|
76
163
|
sub: null,
|
|
77
164
|
epoch: 0,
|
|
165
|
+
depVersion: 0,
|
|
78
166
|
sigPrev: null,
|
|
79
167
|
sigNext: null,
|
|
80
168
|
subPrev: null,
|
|
@@ -159,9 +247,28 @@ function safeInvoke(sub) {
|
|
|
159
247
|
try {
|
|
160
248
|
sub();
|
|
161
249
|
} catch (err) {
|
|
162
|
-
|
|
250
|
+
const s = sub;
|
|
251
|
+
reportError(err, { phase: s._errorPhase ?? "effect", name: s.__name, node: s._errorNode });
|
|
163
252
|
}
|
|
164
253
|
}
|
|
254
|
+
function depsChanged(sub) {
|
|
255
|
+
let node = sub.depsHead ?? null;
|
|
256
|
+
if (node === null) return true;
|
|
257
|
+
while (node !== null) {
|
|
258
|
+
const sig = node.sig;
|
|
259
|
+
if (sig === null) return true;
|
|
260
|
+
if (sig._d === true && sig._validate !== void 0) {
|
|
261
|
+
try {
|
|
262
|
+
sig._validate();
|
|
263
|
+
} catch {
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (node.depVersion !== sig.__v) return true;
|
|
268
|
+
node = node.subNext;
|
|
269
|
+
}
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
165
272
|
var suspendDepth = 0;
|
|
166
273
|
var trackingSuspended = false;
|
|
167
274
|
function suspendTracking() {
|
|
@@ -242,7 +349,7 @@ function track(effectFn, subscriber) {
|
|
|
242
349
|
const sub = subscriber;
|
|
243
350
|
return sub._dispose ?? (sub._dispose = () => cleanup(subscriber));
|
|
244
351
|
}
|
|
245
|
-
function reactiveBinding(commit) {
|
|
352
|
+
function reactiveBinding(commit, ownerNode) {
|
|
246
353
|
const run = () => {
|
|
247
354
|
const s = subscriber;
|
|
248
355
|
if (s._disposed || s._reentrant) return;
|
|
@@ -258,13 +365,17 @@ function reactiveBinding(commit) {
|
|
|
258
365
|
subscriber.depsTail = null;
|
|
259
366
|
subscriber._epoch = 0;
|
|
260
367
|
subscriber._structDirty = false;
|
|
368
|
+
subscriber._hasComputedDep = false;
|
|
261
369
|
subscriber._runEpoch = 0;
|
|
262
370
|
subscriber._runs = 0;
|
|
263
371
|
subscriber._reentrant = false;
|
|
264
372
|
subscriber._disposed = false;
|
|
373
|
+
subscriber._errorPhase = "binding";
|
|
374
|
+
subscriber._errorNode = ownerNode;
|
|
265
375
|
run();
|
|
266
376
|
return subscriber._dispose ?? (subscriber._dispose = () => {
|
|
267
377
|
subscriber._disposed = true;
|
|
378
|
+
subscriber._errorNode = void 0;
|
|
268
379
|
cleanup(subscriber);
|
|
269
380
|
});
|
|
270
381
|
}
|
|
@@ -276,9 +387,12 @@ function recordDependency(signal2) {
|
|
|
276
387
|
const active = sig.__activeNode ?? null;
|
|
277
388
|
if (active !== null && active.sub === sub) {
|
|
278
389
|
active.epoch = epoch;
|
|
390
|
+
active.depVersion = sig.__v ?? Number.NaN;
|
|
279
391
|
return;
|
|
280
392
|
}
|
|
393
|
+
if (sig._validate !== void 0) sub._hasComputedDep = true;
|
|
281
394
|
const node = allocNode(signal2, sub, epoch);
|
|
395
|
+
node.depVersion = sig.__v ?? Number.NaN;
|
|
282
396
|
node.prevActive = active;
|
|
283
397
|
sig.__activeNode = node;
|
|
284
398
|
linkSub(sub, node);
|
|
@@ -297,9 +411,10 @@ function cleanup(subscriber) {
|
|
|
297
411
|
node = next;
|
|
298
412
|
}
|
|
299
413
|
}
|
|
300
|
-
var maxSubscriberRepeats =
|
|
414
|
+
var maxSubscriberRepeats = 1e3;
|
|
301
415
|
var maxDrainIterations = 1e6;
|
|
302
416
|
var drainEpoch = 0;
|
|
417
|
+
var quarantined = /* @__PURE__ */ new Set();
|
|
303
418
|
function setMaxSubscriberRepeats(n) {
|
|
304
419
|
const prev = maxSubscriberRepeats;
|
|
305
420
|
if (Number.isFinite(n) && n > 0) maxSubscriberRepeats = Math.floor(n);
|
|
@@ -321,19 +436,21 @@ function tickRepeat(sub) {
|
|
|
321
436
|
return s._runs > maxSubscriberRepeats;
|
|
322
437
|
}
|
|
323
438
|
function cycleError(sub) {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
`
|
|
328
|
-
)
|
|
329
|
-
|
|
439
|
+
const name = sub.__name ?? "<unnamed>";
|
|
440
|
+
reportError(
|
|
441
|
+
new Error(
|
|
442
|
+
`subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. This subscriber is quarantined for the rest of this update; other pending work still runs.`
|
|
443
|
+
),
|
|
444
|
+
{ phase: "scheduler", name }
|
|
445
|
+
);
|
|
330
446
|
}
|
|
331
447
|
function absoluteDrainError() {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
`
|
|
335
|
-
)
|
|
336
|
-
|
|
448
|
+
reportError(
|
|
449
|
+
new Error(
|
|
450
|
+
`Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
|
|
451
|
+
),
|
|
452
|
+
{ phase: "scheduler" }
|
|
453
|
+
);
|
|
337
454
|
}
|
|
338
455
|
function drainQueue() {
|
|
339
456
|
let i = 0;
|
|
@@ -343,11 +460,14 @@ function drainQueue() {
|
|
|
343
460
|
break;
|
|
344
461
|
}
|
|
345
462
|
const sub = pendingQueue[i++];
|
|
463
|
+
pendingSet.delete(sub);
|
|
464
|
+
if (quarantined.size > 0 && quarantined.has(sub)) continue;
|
|
465
|
+
if (sub._hasComputedDep === true && !depsChanged(sub)) continue;
|
|
346
466
|
if (tickRepeat(sub)) {
|
|
347
467
|
cycleError(sub);
|
|
348
|
-
|
|
468
|
+
quarantined.add(sub);
|
|
469
|
+
continue;
|
|
349
470
|
}
|
|
350
|
-
pendingSet.delete(sub);
|
|
351
471
|
safeInvoke(sub);
|
|
352
472
|
}
|
|
353
473
|
}
|
|
@@ -362,6 +482,7 @@ function drainNotificationQueue() {
|
|
|
362
482
|
if (notifyDepth === 0) {
|
|
363
483
|
pendingQueue.length = 0;
|
|
364
484
|
pendingSet.clear();
|
|
485
|
+
if (quarantined.size > 0) quarantined.clear();
|
|
365
486
|
}
|
|
366
487
|
}
|
|
367
488
|
}
|
|
@@ -455,6 +576,7 @@ function notifySubscribers(signal2) {
|
|
|
455
576
|
if (notifyDepth === 0) {
|
|
456
577
|
pendingQueue.length = 0;
|
|
457
578
|
pendingSet.clear();
|
|
579
|
+
if (quarantined.size > 0) quarantined.clear();
|
|
458
580
|
}
|
|
459
581
|
}
|
|
460
582
|
}
|
|
@@ -481,14 +603,14 @@ function forEachSubscriber(signal2, visit) {
|
|
|
481
603
|
}
|
|
482
604
|
|
|
483
605
|
// src/reactivity/track.ts
|
|
484
|
-
var
|
|
485
|
-
var _runtimeVersion = true ? "
|
|
606
|
+
var _isDev2 = isDev();
|
|
607
|
+
var _runtimeVersion = true ? "4.0.0" : "dev";
|
|
486
608
|
var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
|
|
487
609
|
function resolveReactiveApi() {
|
|
488
610
|
const g = globalThis;
|
|
489
611
|
const existing = g[REGISTRY_KEY];
|
|
490
612
|
if (existing) {
|
|
491
|
-
if (
|
|
613
|
+
if (_isDev2 && !existing.__dupWarned) {
|
|
492
614
|
existing.__dupWarned = true;
|
|
493
615
|
devWarn(
|
|
494
616
|
`Multiple instances of the reactive runtime detected on this page (active: ${existing.version}, duplicate: ${_runtimeVersion}). Reactivity still works \u2014 all copies share the first one \u2014 but de-duplicate sibujs in your bundler (e.g. Vite optimizeDeps.exclude: ['sibujs'] or resolve.dedupe: ['sibujs']).`
|
|
@@ -573,6 +695,21 @@ function derived(getter, options) {
|
|
|
573
695
|
}, markDirty);
|
|
574
696
|
const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
575
697
|
let evaluating = false;
|
|
698
|
+
const validate = () => {
|
|
699
|
+
if (!cs._d) return;
|
|
700
|
+
const oldValue = cs._v;
|
|
701
|
+
evaluating = true;
|
|
702
|
+
try {
|
|
703
|
+
retrack2(recompute, markDirty);
|
|
704
|
+
if (!Object.is(oldValue, cs._v)) cs.__v++;
|
|
705
|
+
} finally {
|
|
706
|
+
evaluating = false;
|
|
707
|
+
}
|
|
708
|
+
if (hook && !Object.is(oldValue, cs._v)) {
|
|
709
|
+
hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
cs._validate = validate;
|
|
576
713
|
function computedGetter() {
|
|
577
714
|
if (evaluating) {
|
|
578
715
|
throw new Error(
|
|
@@ -580,32 +717,11 @@ function derived(getter, options) {
|
|
|
580
717
|
);
|
|
581
718
|
}
|
|
582
719
|
if (isTrackingSuspended2()) {
|
|
583
|
-
if (cs._d)
|
|
584
|
-
const prev = cs._v;
|
|
585
|
-
evaluating = true;
|
|
586
|
-
try {
|
|
587
|
-
retrack2(recompute, markDirty);
|
|
588
|
-
if (!Object.is(prev, cs._v)) cs.__v++;
|
|
589
|
-
} finally {
|
|
590
|
-
evaluating = false;
|
|
591
|
-
}
|
|
592
|
-
}
|
|
720
|
+
if (cs._d) validate();
|
|
593
721
|
return cs._v;
|
|
594
722
|
}
|
|
723
|
+
if (cs._d) validate();
|
|
595
724
|
recordDependency2(cs);
|
|
596
|
-
if (cs._d) {
|
|
597
|
-
const oldValue = cs._v;
|
|
598
|
-
evaluating = true;
|
|
599
|
-
try {
|
|
600
|
-
retrack2(recompute, markDirty);
|
|
601
|
-
if (!Object.is(oldValue, cs._v)) cs.__v++;
|
|
602
|
-
} finally {
|
|
603
|
-
evaluating = false;
|
|
604
|
-
}
|
|
605
|
-
if (hook && oldValue !== cs._v) {
|
|
606
|
-
hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
725
|
return cs._v;
|
|
610
726
|
}
|
|
611
727
|
if (debugName) {
|
|
@@ -627,20 +743,24 @@ function detectSSRShared() {
|
|
|
627
743
|
const getBuiltin = process.getBuiltinModule;
|
|
628
744
|
if (typeof getBuiltin === "function") {
|
|
629
745
|
mod = getBuiltin("node:async_hooks");
|
|
630
|
-
} else {
|
|
631
|
-
|
|
632
|
-
if (req) mod = req("node:async_hooks");
|
|
746
|
+
} else if (typeof require === "function") {
|
|
747
|
+
mod = require("async_hooks");
|
|
633
748
|
}
|
|
634
749
|
if (mod) detected = new mod.AsyncLocalStorage();
|
|
635
750
|
}
|
|
636
751
|
} catch {
|
|
637
752
|
detected = null;
|
|
638
753
|
}
|
|
639
|
-
return {
|
|
754
|
+
return {
|
|
755
|
+
als: detected,
|
|
756
|
+
fallbackStore: { ssr: false, suspenseIdCounter: 0, locale: void 0 },
|
|
757
|
+
fallbackDepth: 0
|
|
758
|
+
};
|
|
640
759
|
}
|
|
641
760
|
var _shared = globalThis[SSR_KEY] ?? (globalThis[SSR_KEY] = detectSSRShared());
|
|
642
761
|
var als = _shared.als;
|
|
643
762
|
var fallbackStore = _shared.fallbackStore;
|
|
763
|
+
_shared.fallbackDepth ?? (_shared.fallbackDepth = 0);
|
|
644
764
|
function getSSRStore() {
|
|
645
765
|
if (als) {
|
|
646
766
|
const s = als.getStore();
|
|
@@ -674,7 +794,7 @@ function flushUserCleanups(ctx) {
|
|
|
674
794
|
try {
|
|
675
795
|
list[i]();
|
|
676
796
|
} catch (err) {
|
|
677
|
-
|
|
797
|
+
reportError(err, { phase: "cleanup" });
|
|
678
798
|
}
|
|
679
799
|
}
|
|
680
800
|
}
|
|
@@ -687,11 +807,12 @@ function drainReruns(ctx) {
|
|
|
687
807
|
} while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
|
|
688
808
|
if (ctx.rerunPending) {
|
|
689
809
|
ctx.rerunPending = false;
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
`
|
|
693
|
-
)
|
|
694
|
-
|
|
810
|
+
reportError(
|
|
811
|
+
new Error(
|
|
812
|
+
`effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
|
|
813
|
+
),
|
|
814
|
+
{ phase: "effect" }
|
|
815
|
+
);
|
|
695
816
|
}
|
|
696
817
|
}
|
|
697
818
|
function disposeEffect(ctx) {
|
|
@@ -768,6 +889,8 @@ function effect(effectFn, options) {
|
|
|
768
889
|
sub.depsTail = null;
|
|
769
890
|
sub._epoch = 0;
|
|
770
891
|
sub._structDirty = false;
|
|
892
|
+
sub._hasComputedDep = false;
|
|
893
|
+
sub._errorPhase = "effect";
|
|
771
894
|
sub._runEpoch = 0;
|
|
772
895
|
sub._runs = 0;
|
|
773
896
|
ctx.subscriber = sub;
|
|
@@ -836,70 +959,72 @@ var isBatching = API2.isBatching;
|
|
|
836
959
|
|
|
837
960
|
// src/core/signals/signal.ts
|
|
838
961
|
var _g2 = globalThis;
|
|
839
|
-
var
|
|
962
|
+
var _isDev3 = isDev();
|
|
840
963
|
function signal(initial, options) {
|
|
841
|
-
const
|
|
964
|
+
const state2 = {
|
|
842
965
|
value: initial,
|
|
843
966
|
__v: 0,
|
|
844
967
|
__sc: 0,
|
|
845
968
|
subsHead: null,
|
|
846
969
|
subsTail: null,
|
|
847
970
|
__activeNode: null,
|
|
848
|
-
__name: void 0
|
|
971
|
+
__name: void 0,
|
|
972
|
+
_d: false,
|
|
973
|
+
_validate: void 0
|
|
849
974
|
};
|
|
850
|
-
const debugName =
|
|
975
|
+
const debugName = _isDev3 ? options?.name : void 0;
|
|
851
976
|
const equalsFn = options?.equals;
|
|
852
|
-
if (debugName)
|
|
977
|
+
if (debugName) state2.__name = debugName;
|
|
853
978
|
function get() {
|
|
854
|
-
recordDependency2(
|
|
855
|
-
return
|
|
979
|
+
recordDependency2(state2);
|
|
980
|
+
return state2.value;
|
|
856
981
|
}
|
|
857
|
-
get.__signal =
|
|
982
|
+
get.__signal = state2;
|
|
858
983
|
if (debugName) get.__name = debugName;
|
|
859
984
|
let set;
|
|
860
985
|
if (equalsFn) {
|
|
861
986
|
set = (next) => {
|
|
862
|
-
const prev =
|
|
987
|
+
const prev = state2.value;
|
|
863
988
|
const newValue = typeof next === "function" ? next(prev) : next;
|
|
864
989
|
if (equalsFn(prev, newValue)) return;
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
if (
|
|
990
|
+
state2.value = newValue;
|
|
991
|
+
state2.__v++;
|
|
992
|
+
if (_isDev3) {
|
|
868
993
|
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
869
|
-
if (hook) hook.emit("signal:update", { signal:
|
|
994
|
+
if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
|
|
870
995
|
}
|
|
871
|
-
if (!enqueueBatchedSignal(
|
|
872
|
-
notifySubscribers2(
|
|
996
|
+
if (!enqueueBatchedSignal(state2)) {
|
|
997
|
+
notifySubscribers2(state2);
|
|
873
998
|
}
|
|
874
999
|
};
|
|
875
|
-
} else if (
|
|
1000
|
+
} else if (_isDev3) {
|
|
876
1001
|
set = (next) => {
|
|
877
|
-
const prev =
|
|
1002
|
+
const prev = state2.value;
|
|
878
1003
|
const newValue = typeof next === "function" ? next(prev) : next;
|
|
879
1004
|
if (Object.is(newValue, prev)) return;
|
|
880
|
-
|
|
881
|
-
|
|
1005
|
+
state2.value = newValue;
|
|
1006
|
+
state2.__v++;
|
|
882
1007
|
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
883
|
-
if (hook) hook.emit("signal:update", { signal:
|
|
884
|
-
if (!enqueueBatchedSignal(
|
|
885
|
-
notifySubscribers2(
|
|
1008
|
+
if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
|
|
1009
|
+
if (!enqueueBatchedSignal(state2)) {
|
|
1010
|
+
notifySubscribers2(state2);
|
|
886
1011
|
}
|
|
887
1012
|
};
|
|
888
1013
|
} else {
|
|
889
1014
|
set = (next) => {
|
|
890
|
-
const prev =
|
|
1015
|
+
const prev = state2.value;
|
|
891
1016
|
const newValue = typeof next === "function" ? next(prev) : next;
|
|
892
1017
|
if (Object.is(newValue, prev)) return;
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
if (!enqueueBatchedSignal(
|
|
896
|
-
notifySubscribers2(
|
|
1018
|
+
state2.value = newValue;
|
|
1019
|
+
state2.__v++;
|
|
1020
|
+
if (!enqueueBatchedSignal(state2)) {
|
|
1021
|
+
notifySubscribers2(state2);
|
|
897
1022
|
}
|
|
898
1023
|
};
|
|
899
1024
|
}
|
|
900
|
-
if (
|
|
1025
|
+
if (_isDev3) {
|
|
901
1026
|
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
902
|
-
if (hook) hook.emit("signal:create", { signal:
|
|
1027
|
+
if (hook) hook.emit("signal:create", { signal: state2, name: debugName, getter: get, initial });
|
|
903
1028
|
}
|
|
904
1029
|
return [get, set];
|
|
905
1030
|
}
|
|
@@ -910,6 +1035,50 @@ function globalSingleton(key, create) {
|
|
|
910
1035
|
return g[key] ?? (g[key] = create());
|
|
911
1036
|
}
|
|
912
1037
|
|
|
1038
|
+
// src/data/abort.ts
|
|
1039
|
+
function isAbortError(error) {
|
|
1040
|
+
return typeof error === "object" && error !== null && error.name === "AbortError";
|
|
1041
|
+
}
|
|
1042
|
+
function createAbortError(message = "Aborted") {
|
|
1043
|
+
if (typeof DOMException !== "undefined") {
|
|
1044
|
+
return new DOMException(message, "AbortError");
|
|
1045
|
+
}
|
|
1046
|
+
const error = new Error(message);
|
|
1047
|
+
error.name = "AbortError";
|
|
1048
|
+
return error;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
// src/data/callbacks.ts
|
|
1052
|
+
function reportCallbackError(label, error) {
|
|
1053
|
+
if (typeof console !== "undefined") {
|
|
1054
|
+
console.error(`[SibuJS data] ${label} threw. The operation's own status is unaffected.`, error);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
function runCallback(label, fn) {
|
|
1058
|
+
try {
|
|
1059
|
+
fn();
|
|
1060
|
+
} catch (error) {
|
|
1061
|
+
reportCallbackError(label, error);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
function runSelect(label, fn) {
|
|
1065
|
+
try {
|
|
1066
|
+
return { ok: true, value: fn() };
|
|
1067
|
+
} catch (error) {
|
|
1068
|
+
reportCallbackError(label, error);
|
|
1069
|
+
return { ok: false };
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
function notifyListeners(listeners, label) {
|
|
1073
|
+
for (const listener of Array.from(listeners)) {
|
|
1074
|
+
try {
|
|
1075
|
+
listener();
|
|
1076
|
+
} catch (error) {
|
|
1077
|
+
reportCallbackError(label, error);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
|
|
913
1082
|
// src/data/retry.ts
|
|
914
1083
|
function calculateDelay(attempt, strategy, baseDelay, maxDelay, jitter) {
|
|
915
1084
|
let delay;
|
|
@@ -942,39 +1111,54 @@ async function withRetry(fn, options, onRetry, signal2) {
|
|
|
942
1111
|
const shouldRetry = options?.shouldRetry ?? (() => true);
|
|
943
1112
|
let lastError;
|
|
944
1113
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
945
|
-
if (signal2?.aborted) throw
|
|
1114
|
+
if (signal2?.aborted) throw createAbortError();
|
|
946
1115
|
try {
|
|
947
|
-
if (signal2?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
948
1116
|
return await fn();
|
|
949
1117
|
} catch (error) {
|
|
950
1118
|
lastError = error;
|
|
1119
|
+
if (signal2?.aborted) throw createAbortError();
|
|
1120
|
+
if (isAbortError(error)) throw error;
|
|
951
1121
|
if (attempt >= maxRetries || !shouldRetry(error, attempt)) throw error;
|
|
952
1122
|
const delay = calculateDelay(attempt, strategy, baseDelay, maxDelay, jitter);
|
|
953
1123
|
onRetry?.(error, attempt, delay);
|
|
954
|
-
await
|
|
955
|
-
let onAbort = null;
|
|
956
|
-
const timer = setTimeout(() => {
|
|
957
|
-
if (onAbort && signal2) signal2.removeEventListener("abort", onAbort);
|
|
958
|
-
resolve();
|
|
959
|
-
}, delay);
|
|
960
|
-
if (signal2) {
|
|
961
|
-
onAbort = () => {
|
|
962
|
-
clearTimeout(timer);
|
|
963
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
964
|
-
};
|
|
965
|
-
signal2.addEventListener("abort", onAbort, { once: true });
|
|
966
|
-
}
|
|
967
|
-
});
|
|
1124
|
+
await waitForRetryDelay(delay, signal2);
|
|
968
1125
|
}
|
|
969
1126
|
}
|
|
970
1127
|
throw lastError;
|
|
971
1128
|
}
|
|
1129
|
+
function waitForRetryDelay(delay, signal2) {
|
|
1130
|
+
if (signal2?.aborted) return Promise.reject(createAbortError());
|
|
1131
|
+
return new Promise((resolve, reject) => {
|
|
1132
|
+
let settled = false;
|
|
1133
|
+
const finish = (action) => {
|
|
1134
|
+
if (settled) return;
|
|
1135
|
+
settled = true;
|
|
1136
|
+
clearTimeout(timer);
|
|
1137
|
+
if (signal2) signal2.removeEventListener("abort", onAbort);
|
|
1138
|
+
action();
|
|
1139
|
+
};
|
|
1140
|
+
const onAbort = () => finish(() => reject(createAbortError()));
|
|
1141
|
+
const timer = setTimeout(() => finish(resolve), delay);
|
|
1142
|
+
if (signal2) {
|
|
1143
|
+
signal2.addEventListener("abort", onAbort, { once: true });
|
|
1144
|
+
if (signal2.aborted) onAbort();
|
|
1145
|
+
}
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
972
1148
|
|
|
973
1149
|
// src/data/query.ts
|
|
974
1150
|
var globalQueryCache = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.query.cache.v1"), () => /* @__PURE__ */ new Map());
|
|
975
1151
|
function getActiveQueryCache() {
|
|
976
1152
|
return getRequestScopedCache("query") ?? globalQueryCache;
|
|
977
1153
|
}
|
|
1154
|
+
function abandonEntry(entry) {
|
|
1155
|
+
if (entry.gcTimer) clearTimeout(entry.gcTimer);
|
|
1156
|
+
entry.gcTimer = null;
|
|
1157
|
+
entry.controller?.abort();
|
|
1158
|
+
entry.controller = null;
|
|
1159
|
+
entry.promise = null;
|
|
1160
|
+
entry.generation++;
|
|
1161
|
+
}
|
|
978
1162
|
function getOrCreateEntry(cache, key, initialData) {
|
|
979
1163
|
let entry = cache.get(key);
|
|
980
1164
|
if (!entry) {
|
|
@@ -986,7 +1170,9 @@ function getOrCreateEntry(cache, key, initialData) {
|
|
|
986
1170
|
gcTimer: null,
|
|
987
1171
|
promise: null,
|
|
988
1172
|
listeners: /* @__PURE__ */ new Set(),
|
|
989
|
-
refetchers: /* @__PURE__ */ new Set()
|
|
1173
|
+
refetchers: /* @__PURE__ */ new Set(),
|
|
1174
|
+
controller: null,
|
|
1175
|
+
generation: 0
|
|
990
1176
|
};
|
|
991
1177
|
cache.set(key, entry);
|
|
992
1178
|
}
|
|
@@ -1012,9 +1198,10 @@ function query(key, fetcher, options = {}) {
|
|
|
1012
1198
|
const [data, setData] = signal(initialData);
|
|
1013
1199
|
const [isFetching, setIsFetching] = signal(false);
|
|
1014
1200
|
const [error, setError] = signal(void 0);
|
|
1015
|
-
let abortController = null;
|
|
1016
1201
|
let disposed = false;
|
|
1017
1202
|
let currentKey = null;
|
|
1203
|
+
let attachedEntry = null;
|
|
1204
|
+
let attachedKey = null;
|
|
1018
1205
|
let intervalTimer = null;
|
|
1019
1206
|
const loading = derived(() => isFetching() && data() === void 0);
|
|
1020
1207
|
const isStale = derived(() => {
|
|
@@ -1024,82 +1211,128 @@ function query(key, fetcher, options = {}) {
|
|
|
1024
1211
|
if (!entry || entry.dataUpdatedAt === 0) return true;
|
|
1025
1212
|
return Date.now() - entry.dataUpdatedAt >= staleTime;
|
|
1026
1213
|
});
|
|
1214
|
+
function detachFromEntry() {
|
|
1215
|
+
const entry = attachedEntry;
|
|
1216
|
+
const key2 = attachedKey;
|
|
1217
|
+
if (!entry) return;
|
|
1218
|
+
attachedEntry = null;
|
|
1219
|
+
attachedKey = null;
|
|
1220
|
+
entry.listeners.delete(onCacheUpdate);
|
|
1221
|
+
entry.refetchers.delete(doFetch);
|
|
1222
|
+
entry.subscribers = Math.max(0, entry.subscribers - 1);
|
|
1223
|
+
if (entry.subscribers > 0 || cacheTime < 0 || key2 === null) return;
|
|
1224
|
+
if (cache.get(key2) !== entry) return;
|
|
1225
|
+
if (entry.gcTimer !== null) clearTimeout(entry.gcTimer);
|
|
1226
|
+
entry.gcTimer = setTimeout(() => {
|
|
1227
|
+
const current = cache.get(key2);
|
|
1228
|
+
if (current === entry && current.subscribers <= 0) {
|
|
1229
|
+
abandonEntry(current);
|
|
1230
|
+
cache.delete(key2);
|
|
1231
|
+
}
|
|
1232
|
+
}, cacheTime);
|
|
1233
|
+
entry.gcTimer.unref?.();
|
|
1234
|
+
}
|
|
1235
|
+
function attachToEntry(entry, key2) {
|
|
1236
|
+
if (attachedEntry === entry) {
|
|
1237
|
+
if (entry.gcTimer !== null) {
|
|
1238
|
+
clearTimeout(entry.gcTimer);
|
|
1239
|
+
entry.gcTimer = null;
|
|
1240
|
+
}
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
detachFromEntry();
|
|
1244
|
+
attachedEntry = entry;
|
|
1245
|
+
attachedKey = key2;
|
|
1246
|
+
entry.subscribers++;
|
|
1247
|
+
entry.listeners.add(onCacheUpdate);
|
|
1248
|
+
entry.refetchers.add(doFetch);
|
|
1249
|
+
if (entry.gcTimer !== null) {
|
|
1250
|
+
clearTimeout(entry.gcTimer);
|
|
1251
|
+
entry.gcTimer = null;
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1027
1254
|
async function doFetch() {
|
|
1028
1255
|
if (disposed || !currentKey || !enabled) return;
|
|
1029
1256
|
const key2 = currentKey;
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
entry = getOrCreateEntry(cache, key2);
|
|
1033
|
-
entry.listeners.add(onCacheUpdate);
|
|
1034
|
-
entry.refetchers.add(doFetch);
|
|
1035
|
-
}
|
|
1257
|
+
const entry = getOrCreateEntry(cache, key2);
|
|
1258
|
+
attachToEntry(entry, key2);
|
|
1036
1259
|
if (entry.promise) {
|
|
1037
1260
|
setIsFetching(true);
|
|
1038
1261
|
const captured = entry.promise;
|
|
1039
1262
|
try {
|
|
1040
1263
|
await captured;
|
|
1041
|
-
if (disposed || currentKey !== key2) return;
|
|
1042
|
-
if (entry.promise === captured) {
|
|
1043
|
-
onCacheUpdate();
|
|
1044
|
-
if (entry.error) onError?.(entry.error);
|
|
1045
|
-
else if (entry.data !== void 0) onSuccess?.(entry.data);
|
|
1046
|
-
}
|
|
1047
1264
|
} catch {
|
|
1048
|
-
|
|
1049
|
-
if (
|
|
1265
|
+
} finally {
|
|
1266
|
+
if (!disposed && currentKey === key2) {
|
|
1050
1267
|
onCacheUpdate();
|
|
1051
|
-
if (entry.error)
|
|
1268
|
+
if (entry.error) {
|
|
1269
|
+
const settledError = entry.error;
|
|
1270
|
+
runCallback("query onError", () => onError?.(settledError));
|
|
1271
|
+
} else if (entry.data !== void 0) {
|
|
1272
|
+
const settledData = entry.data;
|
|
1273
|
+
runCallback("query onSuccess", () => onSuccess?.(settledData));
|
|
1274
|
+
}
|
|
1275
|
+
runCallback("query onSettled", () => onSettled?.());
|
|
1052
1276
|
}
|
|
1053
|
-
} finally {
|
|
1054
|
-
if (!disposed && currentKey === key2) onSettled?.();
|
|
1055
1277
|
}
|
|
1056
1278
|
return;
|
|
1057
1279
|
}
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
const
|
|
1280
|
+
entry.controller = new AbortController();
|
|
1281
|
+
const signal2 = entry.controller.signal;
|
|
1282
|
+
const generation = ++entry.generation;
|
|
1061
1283
|
setIsFetching(true);
|
|
1062
1284
|
let promise;
|
|
1063
1285
|
try {
|
|
1064
1286
|
promise = withRetry(() => fetcher({ signal: signal2, key: key2 }), retryOptions, void 0, signal2);
|
|
1065
1287
|
} catch (err) {
|
|
1066
1288
|
setIsFetching(false);
|
|
1289
|
+
if (isAbortError(err)) return;
|
|
1067
1290
|
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
1068
1291
|
entry.error = errorObj;
|
|
1069
|
-
onError?.(errorObj);
|
|
1070
|
-
onSettled?.();
|
|
1292
|
+
runCallback("query onError", () => onError?.(errorObj));
|
|
1293
|
+
runCallback("query onSettled", () => onSettled?.());
|
|
1071
1294
|
return;
|
|
1072
1295
|
}
|
|
1073
1296
|
entry.promise = promise;
|
|
1074
1297
|
try {
|
|
1075
1298
|
const result = await promise;
|
|
1299
|
+
if (entry.generation !== generation) return;
|
|
1076
1300
|
entry.promise = null;
|
|
1077
|
-
|
|
1301
|
+
entry.controller = null;
|
|
1078
1302
|
entry.data = result;
|
|
1079
1303
|
entry.dataUpdatedAt = Date.now();
|
|
1080
1304
|
entry.error = void 0;
|
|
1081
|
-
|
|
1305
|
+
notifyListeners(entry.listeners, "query cache listener");
|
|
1306
|
+
if (disposed || currentKey !== key2) return;
|
|
1307
|
+
const selected = runSelect("query select", () => select ? select(result) : result);
|
|
1082
1308
|
batch(() => {
|
|
1083
|
-
setData(selected);
|
|
1309
|
+
if (selected.ok) setData(selected.value);
|
|
1084
1310
|
setIsFetching(false);
|
|
1085
1311
|
setError(void 0);
|
|
1086
1312
|
});
|
|
1087
|
-
|
|
1088
|
-
onSuccess?.(result);
|
|
1313
|
+
runCallback("query onSuccess", () => onSuccess?.(result));
|
|
1089
1314
|
} catch (err) {
|
|
1315
|
+
if (entry.generation !== generation) return;
|
|
1090
1316
|
entry.promise = null;
|
|
1091
|
-
|
|
1092
|
-
if (err
|
|
1317
|
+
entry.controller = null;
|
|
1318
|
+
if (isAbortError(err)) {
|
|
1319
|
+
notifyListeners(entry.listeners, "query cache listener");
|
|
1320
|
+
if (!disposed && currentKey === key2) setIsFetching(false);
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1093
1323
|
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
1094
1324
|
entry.error = errorObj;
|
|
1325
|
+
notifyListeners(entry.listeners, "query cache listener");
|
|
1326
|
+
if (disposed || currentKey !== key2) return;
|
|
1095
1327
|
batch(() => {
|
|
1096
1328
|
setError(errorObj);
|
|
1097
1329
|
setIsFetching(false);
|
|
1098
1330
|
});
|
|
1099
|
-
|
|
1100
|
-
onError?.(errorObj);
|
|
1331
|
+
runCallback("query onError", () => onError?.(errorObj));
|
|
1101
1332
|
} finally {
|
|
1102
|
-
if (!disposed && currentKey === key2)
|
|
1333
|
+
if (!disposed && currentKey === key2 && entry.generation === generation) {
|
|
1334
|
+
runCallback("query onSettled", () => onSettled?.());
|
|
1335
|
+
}
|
|
1103
1336
|
}
|
|
1104
1337
|
}
|
|
1105
1338
|
function onCacheUpdate() {
|
|
@@ -1114,43 +1347,24 @@ function query(key, fetcher, options = {}) {
|
|
|
1114
1347
|
return;
|
|
1115
1348
|
}
|
|
1116
1349
|
const raw = entry.data;
|
|
1117
|
-
const selected = raw !== void 0 && select ? select(raw) : raw;
|
|
1350
|
+
const selected = raw !== void 0 && select ? runSelect("query select", () => select(raw)) : { ok: true, value: raw };
|
|
1118
1351
|
batch(() => {
|
|
1119
|
-
setData(selected);
|
|
1352
|
+
if (selected.ok) setData(selected.value);
|
|
1120
1353
|
setError(entry.error);
|
|
1121
1354
|
if (!entry.promise) setIsFetching(false);
|
|
1122
1355
|
});
|
|
1123
1356
|
}
|
|
1124
1357
|
const effectCleanup = effect(() => {
|
|
1125
1358
|
const key2 = resolveKey();
|
|
1126
|
-
if (currentKey !== null && currentKey !== key2) {
|
|
1127
|
-
const oldEntry = cache.get(currentKey);
|
|
1128
|
-
if (oldEntry) {
|
|
1129
|
-
oldEntry.listeners.delete(onCacheUpdate);
|
|
1130
|
-
oldEntry.refetchers.delete(doFetch);
|
|
1131
|
-
oldEntry.subscribers--;
|
|
1132
|
-
if (oldEntry.subscribers <= 0 && cacheTime >= 0) {
|
|
1133
|
-
const oldKey = currentKey;
|
|
1134
|
-
if (oldEntry.gcTimer !== null) clearTimeout(oldEntry.gcTimer);
|
|
1135
|
-
oldEntry.gcTimer = setTimeout(() => cache.delete(oldKey), cacheTime);
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
1359
|
const keyChanged = currentKey !== key2;
|
|
1140
1360
|
currentKey = key2;
|
|
1141
1361
|
const entry = getOrCreateEntry(cache, key2, initialData);
|
|
1142
|
-
|
|
1143
|
-
if (entry.gcTimer !== null) {
|
|
1144
|
-
clearTimeout(entry.gcTimer);
|
|
1145
|
-
entry.gcTimer = null;
|
|
1146
|
-
}
|
|
1147
|
-
entry.listeners.add(onCacheUpdate);
|
|
1148
|
-
entry.refetchers.add(doFetch);
|
|
1362
|
+
attachToEntry(entry, key2);
|
|
1149
1363
|
if (entry.data !== void 0) {
|
|
1150
1364
|
const raw = entry.data;
|
|
1151
|
-
const selected = select ? select(raw) : raw;
|
|
1365
|
+
const selected = runSelect("query select", () => select ? select(raw) : raw);
|
|
1152
1366
|
batch(() => {
|
|
1153
|
-
setData(selected);
|
|
1367
|
+
if (selected.ok) setData(selected.value);
|
|
1154
1368
|
setError(entry.error);
|
|
1155
1369
|
});
|
|
1156
1370
|
}
|
|
@@ -1188,22 +1402,9 @@ function query(key, fetcher, options = {}) {
|
|
|
1188
1402
|
function dispose() {
|
|
1189
1403
|
if (disposed) return;
|
|
1190
1404
|
disposed = true;
|
|
1191
|
-
abortController?.abort();
|
|
1192
1405
|
effectCleanup();
|
|
1193
1406
|
if (intervalTimer) clearInterval(intervalTimer);
|
|
1194
|
-
|
|
1195
|
-
const entry = cache.get(currentKey);
|
|
1196
|
-
if (entry) {
|
|
1197
|
-
entry.listeners.delete(onCacheUpdate);
|
|
1198
|
-
entry.refetchers.delete(doFetch);
|
|
1199
|
-
entry.subscribers--;
|
|
1200
|
-
if (entry.subscribers <= 0 && cacheTime >= 0) {
|
|
1201
|
-
const key2 = currentKey;
|
|
1202
|
-
if (entry.gcTimer !== null) clearTimeout(entry.gcTimer);
|
|
1203
|
-
entry.gcTimer = setTimeout(() => cache.delete(key2), cacheTime);
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1407
|
+
detachFromEntry();
|
|
1207
1408
|
if (focusHandler && typeof globalThis.removeEventListener === "function") {
|
|
1208
1409
|
globalThis.removeEventListener("focus", focusHandler);
|
|
1209
1410
|
}
|
|
@@ -1239,21 +1440,21 @@ function setQueryData(key, data) {
|
|
|
1239
1440
|
const newData = typeof data === "function" ? data(entry.data) : data;
|
|
1240
1441
|
entry.data = newData;
|
|
1241
1442
|
entry.dataUpdatedAt = Date.now();
|
|
1242
|
-
|
|
1443
|
+
notifyListeners(entry.listeners, "query cache listener");
|
|
1243
1444
|
}
|
|
1244
1445
|
function clearQueryCache() {
|
|
1245
1446
|
const activeListeners = [];
|
|
1246
1447
|
const activeRefetchers = [];
|
|
1247
1448
|
const activeCache = getActiveQueryCache();
|
|
1248
1449
|
for (const entry of activeCache.values()) {
|
|
1249
|
-
if (entry.gcTimer) clearTimeout(entry.gcTimer);
|
|
1250
1450
|
if (entry.subscribers > 0) {
|
|
1251
1451
|
for (const listener of entry.listeners) activeListeners.push(listener);
|
|
1252
1452
|
for (const refetcher of entry.refetchers) activeRefetchers.push(refetcher);
|
|
1253
1453
|
}
|
|
1454
|
+
abandonEntry(entry);
|
|
1254
1455
|
}
|
|
1255
1456
|
activeCache.clear();
|
|
1256
|
-
|
|
1457
|
+
notifyListeners(activeListeners, "query cache listener");
|
|
1257
1458
|
for (const refetcher of activeRefetchers) {
|
|
1258
1459
|
refetcher().catch((err) => {
|
|
1259
1460
|
if (typeof console !== "undefined") {
|
|
@@ -1265,7 +1466,7 @@ function clearQueryCache() {
|
|
|
1265
1466
|
function __resetQueryCache() {
|
|
1266
1467
|
const activeCache = getActiveQueryCache();
|
|
1267
1468
|
for (const entry of activeCache.values()) {
|
|
1268
|
-
|
|
1469
|
+
abandonEntry(entry);
|
|
1269
1470
|
}
|
|
1270
1471
|
activeCache.clear();
|
|
1271
1472
|
}
|
|
@@ -1280,6 +1481,8 @@ function mutation(mutationFn, options = {}) {
|
|
|
1280
1481
|
const isIdle = derived(() => status() === "idle");
|
|
1281
1482
|
let runId = 0;
|
|
1282
1483
|
let abortController = null;
|
|
1484
|
+
let terminalStatus = "idle";
|
|
1485
|
+
let terminalError;
|
|
1283
1486
|
async function execute(variables) {
|
|
1284
1487
|
abortController?.abort();
|
|
1285
1488
|
abortController = new AbortController();
|
|
@@ -1297,25 +1500,38 @@ function mutation(mutationFn, options = {}) {
|
|
|
1297
1500
|
}
|
|
1298
1501
|
const result = await withRetry(() => mutationFn(variables, signal2), options.retry, void 0, signal2);
|
|
1299
1502
|
if (myRun !== runId) return result;
|
|
1503
|
+
terminalStatus = "success";
|
|
1504
|
+
terminalError = void 0;
|
|
1300
1505
|
batch(() => {
|
|
1301
1506
|
setData(result);
|
|
1302
1507
|
setLoading(false);
|
|
1303
1508
|
setStatus("success");
|
|
1304
1509
|
});
|
|
1305
|
-
options.onSuccess?.(result, variables, context2);
|
|
1306
|
-
options.onSettled?.(result, void 0, variables, context2);
|
|
1510
|
+
runCallback("mutation onSuccess", () => options.onSuccess?.(result, variables, context2));
|
|
1511
|
+
runCallback("mutation onSettled", () => options.onSettled?.(result, void 0, variables, context2));
|
|
1307
1512
|
return result;
|
|
1308
1513
|
} catch (err) {
|
|
1514
|
+
if (isAbortError(err)) {
|
|
1515
|
+
if (myRun === runId) {
|
|
1516
|
+
batch(() => {
|
|
1517
|
+
setLoading(false);
|
|
1518
|
+
setError(terminalError);
|
|
1519
|
+
setStatus(terminalStatus);
|
|
1520
|
+
});
|
|
1521
|
+
}
|
|
1522
|
+
throw err;
|
|
1523
|
+
}
|
|
1309
1524
|
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
1310
|
-
if (errorObj instanceof DOMException && errorObj.name === "AbortError") throw errorObj;
|
|
1311
1525
|
if (myRun !== runId) throw errorObj;
|
|
1526
|
+
terminalStatus = "error";
|
|
1527
|
+
terminalError = errorObj;
|
|
1312
1528
|
batch(() => {
|
|
1313
1529
|
setError(errorObj);
|
|
1314
1530
|
setLoading(false);
|
|
1315
1531
|
setStatus("error");
|
|
1316
1532
|
});
|
|
1317
|
-
options.onError?.(errorObj, variables, context2);
|
|
1318
|
-
options.onSettled?.(void 0, errorObj, variables, context2);
|
|
1533
|
+
runCallback("mutation onError", () => options.onError?.(errorObj, variables, context2));
|
|
1534
|
+
runCallback("mutation onSettled", () => options.onSettled?.(void 0, errorObj, variables, context2));
|
|
1319
1535
|
throw errorObj;
|
|
1320
1536
|
}
|
|
1321
1537
|
}
|
|
@@ -1323,6 +1539,8 @@ function mutation(mutationFn, options = {}) {
|
|
|
1323
1539
|
runId++;
|
|
1324
1540
|
abortController?.abort();
|
|
1325
1541
|
abortController = null;
|
|
1542
|
+
terminalStatus = "idle";
|
|
1543
|
+
terminalError = void 0;
|
|
1326
1544
|
batch(() => {
|
|
1327
1545
|
setData(void 0);
|
|
1328
1546
|
setError(void 0);
|
|
@@ -1338,7 +1556,7 @@ function mutation(mutationFn, options = {}) {
|
|
|
1338
1556
|
isIdle,
|
|
1339
1557
|
mutate: (variables) => {
|
|
1340
1558
|
execute(variables).catch((err) => {
|
|
1341
|
-
if (err
|
|
1559
|
+
if (isAbortError(err)) return;
|
|
1342
1560
|
if (typeof console !== "undefined") {
|
|
1343
1561
|
console.warn("[SibuJS mutation] mutate() failed; check `.error()` signal or onError option.", err);
|
|
1344
1562
|
}
|
|
@@ -1411,10 +1629,17 @@ function infiniteQuery(key, fetcher, options) {
|
|
|
1411
1629
|
setIsFetchingNext(false);
|
|
1412
1630
|
setIsFetchingPrev(false);
|
|
1413
1631
|
});
|
|
1414
|
-
onSuccess?.(newPages);
|
|
1632
|
+
runCallback("infiniteQuery onSuccess", () => onSuccess?.(newPages));
|
|
1415
1633
|
} catch (err) {
|
|
1416
1634
|
if (disposed || myRun !== runId) return;
|
|
1417
|
-
if (err
|
|
1635
|
+
if (isAbortError(err)) {
|
|
1636
|
+
batch(() => {
|
|
1637
|
+
setIsFetching(false);
|
|
1638
|
+
setIsFetchingNext(false);
|
|
1639
|
+
setIsFetchingPrev(false);
|
|
1640
|
+
});
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1418
1643
|
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
1419
1644
|
batch(() => {
|
|
1420
1645
|
setError(errorObj);
|
|
@@ -1422,7 +1647,7 @@ function infiniteQuery(key, fetcher, options) {
|
|
|
1422
1647
|
setIsFetchingNext(false);
|
|
1423
1648
|
setIsFetchingPrev(false);
|
|
1424
1649
|
});
|
|
1425
|
-
onError?.(errorObj);
|
|
1650
|
+
runCallback("infiniteQuery onError", () => onError?.(errorObj));
|
|
1426
1651
|
}
|
|
1427
1652
|
})();
|
|
1428
1653
|
inFlight = promise;
|
|
@@ -1604,7 +1829,7 @@ function resource(sourceOrFetcher, fetcherOrOptions, maybeOptions) {
|
|
|
1604
1829
|
setLoading(true);
|
|
1605
1830
|
setError(void 0);
|
|
1606
1831
|
});
|
|
1607
|
-
options.onStart?.();
|
|
1832
|
+
runCallback("resource onStart", () => options.onStart?.());
|
|
1608
1833
|
try {
|
|
1609
1834
|
const result = await withRetry(() => fetcher(sourceValue, { signal: signal2, prev }), options.retry, void 0, signal2);
|
|
1610
1835
|
if (version !== fetchVersion || disposed) return;
|
|
@@ -1613,10 +1838,10 @@ function resource(sourceOrFetcher, fetcherOrOptions, maybeOptions) {
|
|
|
1613
1838
|
setData(result);
|
|
1614
1839
|
setLoading(false);
|
|
1615
1840
|
});
|
|
1616
|
-
options.onSuccess?.(result);
|
|
1841
|
+
runCallback("resource onSuccess", () => options.onSuccess?.(result));
|
|
1617
1842
|
} catch (err) {
|
|
1618
1843
|
if (version !== fetchVersion || disposed) return;
|
|
1619
|
-
if (err
|
|
1844
|
+
if (isAbortError(err)) {
|
|
1620
1845
|
if (version === fetchVersion) setLoading(false);
|
|
1621
1846
|
return;
|
|
1622
1847
|
}
|
|
@@ -1625,10 +1850,10 @@ function resource(sourceOrFetcher, fetcherOrOptions, maybeOptions) {
|
|
|
1625
1850
|
setError(errorObj);
|
|
1626
1851
|
setLoading(false);
|
|
1627
1852
|
});
|
|
1628
|
-
options.onError?.(errorObj);
|
|
1853
|
+
runCallback("resource onError", () => options.onError?.(errorObj));
|
|
1629
1854
|
} finally {
|
|
1630
1855
|
if (version === fetchVersion) {
|
|
1631
|
-
options.onSettled?.();
|
|
1856
|
+
runCallback("resource onSettled", () => options.onSettled?.());
|
|
1632
1857
|
}
|
|
1633
1858
|
}
|
|
1634
1859
|
}
|
|
@@ -1816,13 +2041,14 @@ async function offlineStore(options) {
|
|
|
1816
2041
|
return data().filter(filter);
|
|
1817
2042
|
}
|
|
1818
2043
|
async function sync() {
|
|
1819
|
-
|
|
2044
|
+
const syncAdapter2 = adapter;
|
|
2045
|
+
if (!syncAdapter2 || isSyncing() || closed) return;
|
|
1820
2046
|
setIsSyncing(true);
|
|
1821
2047
|
try {
|
|
1822
2048
|
const snapshot = await idbGetAllWithKeys(db, "_changes");
|
|
1823
2049
|
if (closed) return;
|
|
1824
2050
|
if (snapshot.length > 0) {
|
|
1825
|
-
const result = await
|
|
2051
|
+
const result = await syncAdapter2.push(snapshot.map((e) => e.value));
|
|
1826
2052
|
if (closed) return;
|
|
1827
2053
|
if (result.ok) {
|
|
1828
2054
|
await idbDeleteKeys(
|
|
@@ -1835,20 +2061,18 @@ async function offlineStore(options) {
|
|
|
1835
2061
|
console.warn(`[offlineStore] push rejected by adapter${result.error ? `: ${result.error}` : ""}`);
|
|
1836
2062
|
}
|
|
1837
2063
|
}
|
|
1838
|
-
const remoteItems = await
|
|
2064
|
+
const remoteItems = await syncAdapter2.pull(lastSynced());
|
|
1839
2065
|
if (closed) return;
|
|
1840
2066
|
const pendingChanges = await idbGetAll(db, "_changes");
|
|
1841
2067
|
if (closed) return;
|
|
1842
|
-
const
|
|
2068
|
+
const pendingByKey = /* @__PURE__ */ new Map();
|
|
1843
2069
|
for (const c of pendingChanges) {
|
|
1844
2070
|
const k = c.item[keyPath];
|
|
1845
|
-
if (k != null)
|
|
2071
|
+
if (k != null) pendingByKey.set(k, c);
|
|
1846
2072
|
}
|
|
1847
|
-
const
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
});
|
|
1851
|
-
await idbPutMany(db, "items", safeRemote);
|
|
2073
|
+
const resolved = await resolveRemoteItems(syncAdapter2, remoteItems, pendingByKey);
|
|
2074
|
+
if (closed) return;
|
|
2075
|
+
await idbPutMany(db, "items", resolved);
|
|
1852
2076
|
if (closed) return;
|
|
1853
2077
|
const now = Date.now();
|
|
1854
2078
|
await idbPut(db, "_meta", now, "lastSynced");
|
|
@@ -1861,6 +2085,42 @@ async function offlineStore(options) {
|
|
|
1861
2085
|
setIsSyncing(false);
|
|
1862
2086
|
}
|
|
1863
2087
|
}
|
|
2088
|
+
async function resolveRemoteItems(activeAdapter, remoteItems, pendingByKey) {
|
|
2089
|
+
const strategy = activeAdapter.conflictStrategy ?? "client-wins";
|
|
2090
|
+
const out = [];
|
|
2091
|
+
for (const remote of remoteItems) {
|
|
2092
|
+
const key = remote[keyPath];
|
|
2093
|
+
const pending = key == null ? void 0 : pendingByKey.get(key);
|
|
2094
|
+
if (!pending) {
|
|
2095
|
+
out.push(remote);
|
|
2096
|
+
continue;
|
|
2097
|
+
}
|
|
2098
|
+
if (strategy === "server-wins") {
|
|
2099
|
+
out.push(remote);
|
|
2100
|
+
continue;
|
|
2101
|
+
}
|
|
2102
|
+
if (strategy === "manual") {
|
|
2103
|
+
if (!activeAdapter.resolveConflict) {
|
|
2104
|
+
if (typeof console !== "undefined") {
|
|
2105
|
+
console.warn(
|
|
2106
|
+
'[offlineStore] conflictStrategy is "manual" but the adapter has no resolveConflict(); keeping the local record for this key (client-wins).'
|
|
2107
|
+
);
|
|
2108
|
+
}
|
|
2109
|
+
continue;
|
|
2110
|
+
}
|
|
2111
|
+
try {
|
|
2112
|
+
const local = pending.type === "delete" ? void 0 : await idbGet(db, "items", key);
|
|
2113
|
+
const choice = await activeAdapter.resolveConflict({ key, local, remote, pending });
|
|
2114
|
+
if (choice !== void 0) out.push(choice);
|
|
2115
|
+
} catch (err) {
|
|
2116
|
+
if (typeof console !== "undefined") {
|
|
2117
|
+
console.warn("[offlineStore] resolveConflict threw; keeping the local record", err);
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
return out;
|
|
2123
|
+
}
|
|
1864
2124
|
function attach(newAdapter) {
|
|
1865
2125
|
adapter = newAdapter;
|
|
1866
2126
|
}
|
|
@@ -1899,10 +2159,18 @@ function syncAdapter(config) {
|
|
|
1899
2159
|
}
|
|
1900
2160
|
|
|
1901
2161
|
// src/core/rendering/context.ts
|
|
2162
|
+
var _isDev4 = isDev();
|
|
1902
2163
|
function context(defaultValue) {
|
|
1903
2164
|
const [getValue, setValue] = signal(defaultValue);
|
|
2165
|
+
const warnIfSSR = (method) => {
|
|
2166
|
+
if (!_isDev4 || !isSSR()) return;
|
|
2167
|
+
devWarn(
|
|
2168
|
+
`context.${method}() called during SSR. SibuJS context() is application-global \u2014 it is NOT isolated per request, so a concurrent request can observe this value. Do not use context() for request-specific SSR data; pass it explicitly or use request-scoped storage instead.`
|
|
2169
|
+
);
|
|
2170
|
+
};
|
|
1904
2171
|
const ctx = {
|
|
1905
2172
|
provide(value) {
|
|
2173
|
+
warnIfSSR("provide");
|
|
1906
2174
|
const previous2 = getValue();
|
|
1907
2175
|
setValue(value);
|
|
1908
2176
|
return () => setValue(previous2);
|
|
@@ -1914,13 +2182,21 @@ function context(defaultValue) {
|
|
|
1914
2182
|
return getValue();
|
|
1915
2183
|
},
|
|
1916
2184
|
set(value) {
|
|
2185
|
+
warnIfSSR("set");
|
|
1917
2186
|
setValue(value);
|
|
1918
2187
|
},
|
|
1919
2188
|
withContext(value, fn) {
|
|
2189
|
+
warnIfSSR("withContext");
|
|
1920
2190
|
const previous2 = getValue();
|
|
1921
2191
|
setValue(value);
|
|
1922
2192
|
try {
|
|
1923
|
-
|
|
2193
|
+
const result = fn();
|
|
2194
|
+
if (_isDev4 && result !== null && typeof result?.then === "function") {
|
|
2195
|
+
devWarn(
|
|
2196
|
+
"context.withContext() received an async callback. Scoping is synchronous only \u2014 the previous value is restored as soon as the callback returns its promise, so anything after an `await` runs outside the scope."
|
|
2197
|
+
);
|
|
2198
|
+
}
|
|
2199
|
+
return result;
|
|
1924
2200
|
} finally {
|
|
1925
2201
|
setValue(previous2);
|
|
1926
2202
|
}
|
|
@@ -2077,10 +2353,12 @@ function stripControlChars(value) {
|
|
|
2077
2353
|
return value.replace(/[\x00-\x20\x7f-\x9f]+/g, "");
|
|
2078
2354
|
}
|
|
2079
2355
|
var SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
|
|
2356
|
+
var OUTER_TRIM = /^[\x00-\x20\x7f-\x9f]+|[\x00-\x20\x7f-\x9f]+$/g;
|
|
2080
2357
|
function sanitizeUrl(url) {
|
|
2081
|
-
const
|
|
2082
|
-
if (!
|
|
2083
|
-
const
|
|
2358
|
+
const probe = stripControlChars(url).trim();
|
|
2359
|
+
if (!probe) return "";
|
|
2360
|
+
const output = url.replace(OUTER_TRIM, "");
|
|
2361
|
+
const lower = probe.toLowerCase();
|
|
2084
2362
|
let schemeEnd = -1;
|
|
2085
2363
|
for (let i = 0; i < lower.length; i++) {
|
|
2086
2364
|
const ch = lower.charCodeAt(i);
|
|
@@ -2090,11 +2368,11 @@ function sanitizeUrl(url) {
|
|
|
2090
2368
|
}
|
|
2091
2369
|
if (ch === 47 || ch === 63 || ch === 35) break;
|
|
2092
2370
|
}
|
|
2093
|
-
if (schemeEnd === -1) return
|
|
2371
|
+
if (schemeEnd === -1) return output;
|
|
2094
2372
|
const scheme = lower.slice(0, schemeEnd + 1);
|
|
2095
|
-
if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return
|
|
2373
|
+
if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return output;
|
|
2096
2374
|
if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
|
|
2097
|
-
return
|
|
2375
|
+
return output;
|
|
2098
2376
|
}
|
|
2099
2377
|
|
|
2100
2378
|
// src/ui/stream.ts
|