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/extras.cjs
CHANGED
|
@@ -175,6 +175,7 @@ __export(extras_exports, {
|
|
|
175
175
|
lazyModule: () => lazyModule,
|
|
176
176
|
loadRemoteModule: () => loadRemoteModule,
|
|
177
177
|
loadWasmModule: () => loadWasmModule,
|
|
178
|
+
loadWasmModuleWithOptions: () => loadWasmModuleWithOptions,
|
|
178
179
|
loaderData: () => loaderData,
|
|
179
180
|
machine: () => machine,
|
|
180
181
|
matchesPattern: () => matchesPattern,
|
|
@@ -338,8 +339,95 @@ function devWarn(message) {
|
|
|
338
339
|
}
|
|
339
340
|
}
|
|
340
341
|
|
|
342
|
+
// src/core/errors.ts
|
|
343
|
+
var ERROR_STATE_KEY = /* @__PURE__ */ Symbol.for("sibujs.runtime-errors.v1");
|
|
344
|
+
function state() {
|
|
345
|
+
const g2 = globalThis;
|
|
346
|
+
const existing = g2[ERROR_STATE_KEY];
|
|
347
|
+
if (existing) return existing;
|
|
348
|
+
const created = { handler: null, handlerDepth: 0 };
|
|
349
|
+
g2[ERROR_STATE_KEY] = created;
|
|
350
|
+
return created;
|
|
351
|
+
}
|
|
352
|
+
function normalizeError(value) {
|
|
353
|
+
if (value instanceof Error) return value;
|
|
354
|
+
const message = typeof value === "string" ? value : `Non-Error value thrown: ${safeStringify(value)}`;
|
|
355
|
+
return new Error(message, { cause: value });
|
|
356
|
+
}
|
|
357
|
+
function safeStringify(value) {
|
|
358
|
+
if (typeof value === "symbol") return value.toString();
|
|
359
|
+
try {
|
|
360
|
+
return typeof value === "object" && value !== null ? JSON.stringify(value) : String(value);
|
|
361
|
+
} catch {
|
|
362
|
+
return Object.prototype.toString.call(value);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
function describe(context2) {
|
|
366
|
+
return context2.name ? `${context2.phase} "${context2.name}"` : context2.phase;
|
|
367
|
+
}
|
|
368
|
+
var REACTIVE_REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
|
|
369
|
+
function untrackedRun(fn) {
|
|
370
|
+
const api = globalThis[REACTIVE_REGISTRY_KEY];
|
|
371
|
+
return api?.untracked ? api.untracked(fn) : fn();
|
|
372
|
+
}
|
|
373
|
+
var PROPAGATE_EVENT = "sibu:error-propagate";
|
|
374
|
+
function claimedByBoundary(error, context2) {
|
|
375
|
+
const raw = context2.node;
|
|
376
|
+
if (!raw || typeof CustomEvent === "undefined") return false;
|
|
377
|
+
const target = resolveEventTarget(raw);
|
|
378
|
+
if (!target) return false;
|
|
379
|
+
try {
|
|
380
|
+
const event = new CustomEvent(PROPAGATE_EVENT, {
|
|
381
|
+
bubbles: true,
|
|
382
|
+
cancelable: true,
|
|
383
|
+
detail: { error, context: context2 }
|
|
384
|
+
});
|
|
385
|
+
return target.dispatchEvent(event) === false;
|
|
386
|
+
} catch {
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
function resolveEventTarget(node) {
|
|
391
|
+
const ELEMENT_NODE = 1;
|
|
392
|
+
let current = node;
|
|
393
|
+
while (current) {
|
|
394
|
+
if (current.nodeType === ELEMENT_NODE && typeof current.dispatchEvent === "function") {
|
|
395
|
+
return current;
|
|
396
|
+
}
|
|
397
|
+
current = current.parentNode;
|
|
398
|
+
}
|
|
399
|
+
const self2 = node;
|
|
400
|
+
return typeof self2.dispatchEvent === "function" ? self2 : null;
|
|
401
|
+
}
|
|
402
|
+
function defaultReport(error, context2) {
|
|
403
|
+
if (typeof console === "undefined") return;
|
|
404
|
+
console.error(`[SibuJS] Uncaught error during ${describe(context2)}: ${error.message}`, error);
|
|
405
|
+
}
|
|
406
|
+
function reportError(error, context2) {
|
|
407
|
+
const err = normalizeError(error);
|
|
408
|
+
untrackedRun(() => {
|
|
409
|
+
if (claimedByBoundary(err, context2)) return;
|
|
410
|
+
const s2 = state();
|
|
411
|
+
const handler = s2.handler;
|
|
412
|
+
if (handler && s2.handlerDepth === 0) {
|
|
413
|
+
s2.handlerDepth++;
|
|
414
|
+
try {
|
|
415
|
+
handler(error instanceof Error ? error : err, context2);
|
|
416
|
+
return;
|
|
417
|
+
} catch (handlerError) {
|
|
418
|
+
if (typeof console !== "undefined") {
|
|
419
|
+
const detail = handlerError instanceof Error ? handlerError.message : String(handlerError);
|
|
420
|
+
console.error(`[SibuJS] runtime error handler itself threw: ${detail}`, handlerError);
|
|
421
|
+
}
|
|
422
|
+
} finally {
|
|
423
|
+
s2.handlerDepth--;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
defaultReport(err, context2);
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
|
|
341
430
|
// src/reactivity/track-core.ts
|
|
342
|
-
var _isDev2 = isDev();
|
|
343
431
|
var POOL_MAX = 4096;
|
|
344
432
|
var nodePool = [];
|
|
345
433
|
function createNode() {
|
|
@@ -347,6 +435,7 @@ function createNode() {
|
|
|
347
435
|
sig: null,
|
|
348
436
|
sub: null,
|
|
349
437
|
epoch: 0,
|
|
438
|
+
depVersion: 0,
|
|
350
439
|
sigPrev: null,
|
|
351
440
|
sigNext: null,
|
|
352
441
|
subPrev: null,
|
|
@@ -431,8 +520,27 @@ function safeInvoke(sub2) {
|
|
|
431
520
|
try {
|
|
432
521
|
sub2();
|
|
433
522
|
} catch (err) {
|
|
434
|
-
|
|
523
|
+
const s2 = sub2;
|
|
524
|
+
reportError(err, { phase: s2._errorPhase ?? "effect", name: s2.__name, node: s2._errorNode });
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
function depsChanged(sub2) {
|
|
528
|
+
let node = sub2.depsHead ?? null;
|
|
529
|
+
if (node === null) return true;
|
|
530
|
+
while (node !== null) {
|
|
531
|
+
const sig = node.sig;
|
|
532
|
+
if (sig === null) return true;
|
|
533
|
+
if (sig._d === true && sig._validate !== void 0) {
|
|
534
|
+
try {
|
|
535
|
+
sig._validate();
|
|
536
|
+
} catch {
|
|
537
|
+
return true;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
if (node.depVersion !== sig.__v) return true;
|
|
541
|
+
node = node.subNext;
|
|
435
542
|
}
|
|
543
|
+
return false;
|
|
436
544
|
}
|
|
437
545
|
var suspendDepth = 0;
|
|
438
546
|
var trackingSuspended = false;
|
|
@@ -514,7 +622,7 @@ function track(effectFn, subscriber) {
|
|
|
514
622
|
const sub2 = subscriber;
|
|
515
623
|
return sub2._dispose ?? (sub2._dispose = () => cleanup(subscriber));
|
|
516
624
|
}
|
|
517
|
-
function reactiveBinding(commit) {
|
|
625
|
+
function reactiveBinding(commit, ownerNode) {
|
|
518
626
|
const run = () => {
|
|
519
627
|
const s2 = subscriber;
|
|
520
628
|
if (s2._disposed || s2._reentrant) return;
|
|
@@ -530,13 +638,17 @@ function reactiveBinding(commit) {
|
|
|
530
638
|
subscriber.depsTail = null;
|
|
531
639
|
subscriber._epoch = 0;
|
|
532
640
|
subscriber._structDirty = false;
|
|
641
|
+
subscriber._hasComputedDep = false;
|
|
533
642
|
subscriber._runEpoch = 0;
|
|
534
643
|
subscriber._runs = 0;
|
|
535
644
|
subscriber._reentrant = false;
|
|
536
645
|
subscriber._disposed = false;
|
|
646
|
+
subscriber._errorPhase = "binding";
|
|
647
|
+
subscriber._errorNode = ownerNode;
|
|
537
648
|
run();
|
|
538
649
|
return subscriber._dispose ?? (subscriber._dispose = () => {
|
|
539
650
|
subscriber._disposed = true;
|
|
651
|
+
subscriber._errorNode = void 0;
|
|
540
652
|
cleanup(subscriber);
|
|
541
653
|
});
|
|
542
654
|
}
|
|
@@ -548,9 +660,12 @@ function recordDependency(signal2) {
|
|
|
548
660
|
const active = sig.__activeNode ?? null;
|
|
549
661
|
if (active !== null && active.sub === sub2) {
|
|
550
662
|
active.epoch = epoch;
|
|
663
|
+
active.depVersion = sig.__v ?? Number.NaN;
|
|
551
664
|
return;
|
|
552
665
|
}
|
|
666
|
+
if (sig._validate !== void 0) sub2._hasComputedDep = true;
|
|
553
667
|
const node = allocNode(signal2, sub2, epoch);
|
|
668
|
+
node.depVersion = sig.__v ?? Number.NaN;
|
|
554
669
|
node.prevActive = active;
|
|
555
670
|
sig.__activeNode = node;
|
|
556
671
|
linkSub(sub2, node);
|
|
@@ -569,9 +684,10 @@ function cleanup(subscriber) {
|
|
|
569
684
|
node = next;
|
|
570
685
|
}
|
|
571
686
|
}
|
|
572
|
-
var maxSubscriberRepeats =
|
|
687
|
+
var maxSubscriberRepeats = 1e3;
|
|
573
688
|
var maxDrainIterations = 1e6;
|
|
574
689
|
var drainEpoch = 0;
|
|
690
|
+
var quarantined = /* @__PURE__ */ new Set();
|
|
575
691
|
function setMaxSubscriberRepeats(n) {
|
|
576
692
|
const prev = maxSubscriberRepeats;
|
|
577
693
|
if (Number.isFinite(n) && n > 0) maxSubscriberRepeats = Math.floor(n);
|
|
@@ -593,19 +709,21 @@ function tickRepeat(sub2) {
|
|
|
593
709
|
return s2._runs > maxSubscriberRepeats;
|
|
594
710
|
}
|
|
595
711
|
function cycleError(sub2) {
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
`
|
|
600
|
-
)
|
|
601
|
-
|
|
712
|
+
const name = sub2.__name ?? "<unnamed>";
|
|
713
|
+
reportError(
|
|
714
|
+
new Error(
|
|
715
|
+
`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.`
|
|
716
|
+
),
|
|
717
|
+
{ phase: "scheduler", name }
|
|
718
|
+
);
|
|
602
719
|
}
|
|
603
720
|
function absoluteDrainError() {
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
`
|
|
607
|
-
)
|
|
608
|
-
|
|
721
|
+
reportError(
|
|
722
|
+
new Error(
|
|
723
|
+
`Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
|
|
724
|
+
),
|
|
725
|
+
{ phase: "scheduler" }
|
|
726
|
+
);
|
|
609
727
|
}
|
|
610
728
|
function drainQueue() {
|
|
611
729
|
let i2 = 0;
|
|
@@ -615,11 +733,14 @@ function drainQueue() {
|
|
|
615
733
|
break;
|
|
616
734
|
}
|
|
617
735
|
const sub2 = pendingQueue[i2++];
|
|
736
|
+
pendingSet.delete(sub2);
|
|
737
|
+
if (quarantined.size > 0 && quarantined.has(sub2)) continue;
|
|
738
|
+
if (sub2._hasComputedDep === true && !depsChanged(sub2)) continue;
|
|
618
739
|
if (tickRepeat(sub2)) {
|
|
619
740
|
cycleError(sub2);
|
|
620
|
-
|
|
741
|
+
quarantined.add(sub2);
|
|
742
|
+
continue;
|
|
621
743
|
}
|
|
622
|
-
pendingSet.delete(sub2);
|
|
623
744
|
safeInvoke(sub2);
|
|
624
745
|
}
|
|
625
746
|
}
|
|
@@ -634,6 +755,7 @@ function drainNotificationQueue() {
|
|
|
634
755
|
if (notifyDepth === 0) {
|
|
635
756
|
pendingQueue.length = 0;
|
|
636
757
|
pendingSet.clear();
|
|
758
|
+
if (quarantined.size > 0) quarantined.clear();
|
|
637
759
|
}
|
|
638
760
|
}
|
|
639
761
|
}
|
|
@@ -727,6 +849,7 @@ function notifySubscribers(signal2) {
|
|
|
727
849
|
if (notifyDepth === 0) {
|
|
728
850
|
pendingQueue.length = 0;
|
|
729
851
|
pendingSet.clear();
|
|
852
|
+
if (quarantined.size > 0) quarantined.clear();
|
|
730
853
|
}
|
|
731
854
|
}
|
|
732
855
|
}
|
|
@@ -753,14 +876,14 @@ function forEachSubscriber(signal2, visit) {
|
|
|
753
876
|
}
|
|
754
877
|
|
|
755
878
|
// src/reactivity/track.ts
|
|
756
|
-
var
|
|
757
|
-
var _runtimeVersion = true ? "
|
|
879
|
+
var _isDev2 = isDev();
|
|
880
|
+
var _runtimeVersion = true ? "4.0.0" : "dev";
|
|
758
881
|
var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
|
|
759
882
|
function resolveReactiveApi() {
|
|
760
883
|
const g2 = globalThis;
|
|
761
884
|
const existing = g2[REGISTRY_KEY];
|
|
762
885
|
if (existing) {
|
|
763
|
-
if (
|
|
886
|
+
if (_isDev2 && !existing.__dupWarned) {
|
|
764
887
|
existing.__dupWarned = true;
|
|
765
888
|
devWarn(
|
|
766
889
|
`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']).`
|
|
@@ -845,6 +968,21 @@ function derived(getter, options) {
|
|
|
845
968
|
}, markDirty);
|
|
846
969
|
const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
847
970
|
let evaluating = false;
|
|
971
|
+
const validate = () => {
|
|
972
|
+
if (!cs._d) return;
|
|
973
|
+
const oldValue = cs._v;
|
|
974
|
+
evaluating = true;
|
|
975
|
+
try {
|
|
976
|
+
retrack2(recompute, markDirty);
|
|
977
|
+
if (!Object.is(oldValue, cs._v)) cs.__v++;
|
|
978
|
+
} finally {
|
|
979
|
+
evaluating = false;
|
|
980
|
+
}
|
|
981
|
+
if (hook && !Object.is(oldValue, cs._v)) {
|
|
982
|
+
hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
|
|
983
|
+
}
|
|
984
|
+
};
|
|
985
|
+
cs._validate = validate;
|
|
848
986
|
function computedGetter() {
|
|
849
987
|
if (evaluating) {
|
|
850
988
|
throw new Error(
|
|
@@ -852,32 +990,11 @@ function derived(getter, options) {
|
|
|
852
990
|
);
|
|
853
991
|
}
|
|
854
992
|
if (isTrackingSuspended2()) {
|
|
855
|
-
if (cs._d)
|
|
856
|
-
const prev = cs._v;
|
|
857
|
-
evaluating = true;
|
|
858
|
-
try {
|
|
859
|
-
retrack2(recompute, markDirty);
|
|
860
|
-
if (!Object.is(prev, cs._v)) cs.__v++;
|
|
861
|
-
} finally {
|
|
862
|
-
evaluating = false;
|
|
863
|
-
}
|
|
864
|
-
}
|
|
993
|
+
if (cs._d) validate();
|
|
865
994
|
return cs._v;
|
|
866
995
|
}
|
|
996
|
+
if (cs._d) validate();
|
|
867
997
|
recordDependency2(cs);
|
|
868
|
-
if (cs._d) {
|
|
869
|
-
const oldValue = cs._v;
|
|
870
|
-
evaluating = true;
|
|
871
|
-
try {
|
|
872
|
-
retrack2(recompute, markDirty);
|
|
873
|
-
if (!Object.is(oldValue, cs._v)) cs.__v++;
|
|
874
|
-
} finally {
|
|
875
|
-
evaluating = false;
|
|
876
|
-
}
|
|
877
|
-
if (hook && oldValue !== cs._v) {
|
|
878
|
-
hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
998
|
return cs._v;
|
|
882
999
|
}
|
|
883
1000
|
if (debugName) {
|
|
@@ -899,20 +1016,24 @@ function detectSSRShared() {
|
|
|
899
1016
|
const getBuiltin = process.getBuiltinModule;
|
|
900
1017
|
if (typeof getBuiltin === "function") {
|
|
901
1018
|
mod = getBuiltin("node:async_hooks");
|
|
902
|
-
} else {
|
|
903
|
-
|
|
904
|
-
if (req) mod = req("node:async_hooks");
|
|
1019
|
+
} else if (typeof require === "function") {
|
|
1020
|
+
mod = require("async_hooks");
|
|
905
1021
|
}
|
|
906
1022
|
if (mod) detected = new mod.AsyncLocalStorage();
|
|
907
1023
|
}
|
|
908
1024
|
} catch {
|
|
909
1025
|
detected = null;
|
|
910
1026
|
}
|
|
911
|
-
return {
|
|
1027
|
+
return {
|
|
1028
|
+
als: detected,
|
|
1029
|
+
fallbackStore: { ssr: false, suspenseIdCounter: 0, locale: void 0 },
|
|
1030
|
+
fallbackDepth: 0
|
|
1031
|
+
};
|
|
912
1032
|
}
|
|
913
1033
|
var _shared = globalThis[SSR_KEY] ?? (globalThis[SSR_KEY] = detectSSRShared());
|
|
914
1034
|
var als = _shared.als;
|
|
915
1035
|
var fallbackStore = _shared.fallbackStore;
|
|
1036
|
+
_shared.fallbackDepth ?? (_shared.fallbackDepth = 0);
|
|
916
1037
|
function getSSRStore() {
|
|
917
1038
|
if (als) {
|
|
918
1039
|
const s2 = als.getStore();
|
|
@@ -946,7 +1067,7 @@ function flushUserCleanups(ctx) {
|
|
|
946
1067
|
try {
|
|
947
1068
|
list[i2]();
|
|
948
1069
|
} catch (err) {
|
|
949
|
-
|
|
1070
|
+
reportError(err, { phase: "cleanup" });
|
|
950
1071
|
}
|
|
951
1072
|
}
|
|
952
1073
|
}
|
|
@@ -959,11 +1080,12 @@ function drainReruns(ctx) {
|
|
|
959
1080
|
} while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
|
|
960
1081
|
if (ctx.rerunPending) {
|
|
961
1082
|
ctx.rerunPending = false;
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
`
|
|
965
|
-
)
|
|
966
|
-
|
|
1083
|
+
reportError(
|
|
1084
|
+
new Error(
|
|
1085
|
+
`effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
|
|
1086
|
+
),
|
|
1087
|
+
{ phase: "effect" }
|
|
1088
|
+
);
|
|
967
1089
|
}
|
|
968
1090
|
}
|
|
969
1091
|
function disposeEffect(ctx) {
|
|
@@ -1040,6 +1162,8 @@ function effect(effectFn, options) {
|
|
|
1040
1162
|
sub2.depsTail = null;
|
|
1041
1163
|
sub2._epoch = 0;
|
|
1042
1164
|
sub2._structDirty = false;
|
|
1165
|
+
sub2._hasComputedDep = false;
|
|
1166
|
+
sub2._errorPhase = "effect";
|
|
1043
1167
|
sub2._runEpoch = 0;
|
|
1044
1168
|
sub2._runs = 0;
|
|
1045
1169
|
ctx.subscriber = sub2;
|
|
@@ -1108,70 +1232,72 @@ var isBatching = API2.isBatching;
|
|
|
1108
1232
|
|
|
1109
1233
|
// src/core/signals/signal.ts
|
|
1110
1234
|
var _g2 = globalThis;
|
|
1111
|
-
var
|
|
1235
|
+
var _isDev3 = isDev();
|
|
1112
1236
|
function signal(initial, options) {
|
|
1113
|
-
const
|
|
1237
|
+
const state2 = {
|
|
1114
1238
|
value: initial,
|
|
1115
1239
|
__v: 0,
|
|
1116
1240
|
__sc: 0,
|
|
1117
1241
|
subsHead: null,
|
|
1118
1242
|
subsTail: null,
|
|
1119
1243
|
__activeNode: null,
|
|
1120
|
-
__name: void 0
|
|
1244
|
+
__name: void 0,
|
|
1245
|
+
_d: false,
|
|
1246
|
+
_validate: void 0
|
|
1121
1247
|
};
|
|
1122
|
-
const debugName =
|
|
1248
|
+
const debugName = _isDev3 ? options?.name : void 0;
|
|
1123
1249
|
const equalsFn = options?.equals;
|
|
1124
|
-
if (debugName)
|
|
1250
|
+
if (debugName) state2.__name = debugName;
|
|
1125
1251
|
function get() {
|
|
1126
|
-
recordDependency2(
|
|
1127
|
-
return
|
|
1252
|
+
recordDependency2(state2);
|
|
1253
|
+
return state2.value;
|
|
1128
1254
|
}
|
|
1129
|
-
get.__signal =
|
|
1255
|
+
get.__signal = state2;
|
|
1130
1256
|
if (debugName) get.__name = debugName;
|
|
1131
1257
|
let set;
|
|
1132
1258
|
if (equalsFn) {
|
|
1133
1259
|
set = (next) => {
|
|
1134
|
-
const prev =
|
|
1260
|
+
const prev = state2.value;
|
|
1135
1261
|
const newValue = typeof next === "function" ? next(prev) : next;
|
|
1136
1262
|
if (equalsFn(prev, newValue)) return;
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
if (
|
|
1263
|
+
state2.value = newValue;
|
|
1264
|
+
state2.__v++;
|
|
1265
|
+
if (_isDev3) {
|
|
1140
1266
|
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1141
|
-
if (hook) hook.emit("signal:update", { signal:
|
|
1267
|
+
if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
|
|
1142
1268
|
}
|
|
1143
|
-
if (!enqueueBatchedSignal(
|
|
1144
|
-
notifySubscribers2(
|
|
1269
|
+
if (!enqueueBatchedSignal(state2)) {
|
|
1270
|
+
notifySubscribers2(state2);
|
|
1145
1271
|
}
|
|
1146
1272
|
};
|
|
1147
|
-
} else if (
|
|
1273
|
+
} else if (_isDev3) {
|
|
1148
1274
|
set = (next) => {
|
|
1149
|
-
const prev =
|
|
1275
|
+
const prev = state2.value;
|
|
1150
1276
|
const newValue = typeof next === "function" ? next(prev) : next;
|
|
1151
1277
|
if (Object.is(newValue, prev)) return;
|
|
1152
|
-
|
|
1153
|
-
|
|
1278
|
+
state2.value = newValue;
|
|
1279
|
+
state2.__v++;
|
|
1154
1280
|
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1155
|
-
if (hook) hook.emit("signal:update", { signal:
|
|
1156
|
-
if (!enqueueBatchedSignal(
|
|
1157
|
-
notifySubscribers2(
|
|
1281
|
+
if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
|
|
1282
|
+
if (!enqueueBatchedSignal(state2)) {
|
|
1283
|
+
notifySubscribers2(state2);
|
|
1158
1284
|
}
|
|
1159
1285
|
};
|
|
1160
1286
|
} else {
|
|
1161
1287
|
set = (next) => {
|
|
1162
|
-
const prev =
|
|
1288
|
+
const prev = state2.value;
|
|
1163
1289
|
const newValue = typeof next === "function" ? next(prev) : next;
|
|
1164
1290
|
if (Object.is(newValue, prev)) return;
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
if (!enqueueBatchedSignal(
|
|
1168
|
-
notifySubscribers2(
|
|
1291
|
+
state2.value = newValue;
|
|
1292
|
+
state2.__v++;
|
|
1293
|
+
if (!enqueueBatchedSignal(state2)) {
|
|
1294
|
+
notifySubscribers2(state2);
|
|
1169
1295
|
}
|
|
1170
1296
|
};
|
|
1171
1297
|
}
|
|
1172
|
-
if (
|
|
1298
|
+
if (_isDev3) {
|
|
1173
1299
|
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1174
|
-
if (hook) hook.emit("signal:create", { signal:
|
|
1300
|
+
if (hook) hook.emit("signal:create", { signal: state2, name: debugName, getter: get, initial });
|
|
1175
1301
|
}
|
|
1176
1302
|
return [get, set];
|
|
1177
1303
|
}
|
|
@@ -1182,6 +1308,50 @@ function globalSingleton(key, create) {
|
|
|
1182
1308
|
return g2[key] ?? (g2[key] = create());
|
|
1183
1309
|
}
|
|
1184
1310
|
|
|
1311
|
+
// src/data/abort.ts
|
|
1312
|
+
function isAbortError(error) {
|
|
1313
|
+
return typeof error === "object" && error !== null && error.name === "AbortError";
|
|
1314
|
+
}
|
|
1315
|
+
function createAbortError(message = "Aborted") {
|
|
1316
|
+
if (typeof DOMException !== "undefined") {
|
|
1317
|
+
return new DOMException(message, "AbortError");
|
|
1318
|
+
}
|
|
1319
|
+
const error = new Error(message);
|
|
1320
|
+
error.name = "AbortError";
|
|
1321
|
+
return error;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
// src/data/callbacks.ts
|
|
1325
|
+
function reportCallbackError(label2, error) {
|
|
1326
|
+
if (typeof console !== "undefined") {
|
|
1327
|
+
console.error(`[SibuJS data] ${label2} threw. The operation's own status is unaffected.`, error);
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
function runCallback(label2, fn) {
|
|
1331
|
+
try {
|
|
1332
|
+
fn();
|
|
1333
|
+
} catch (error) {
|
|
1334
|
+
reportCallbackError(label2, error);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
function runSelect(label2, fn) {
|
|
1338
|
+
try {
|
|
1339
|
+
return { ok: true, value: fn() };
|
|
1340
|
+
} catch (error) {
|
|
1341
|
+
reportCallbackError(label2, error);
|
|
1342
|
+
return { ok: false };
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
function notifyListeners(listeners, label2) {
|
|
1346
|
+
for (const listener of Array.from(listeners)) {
|
|
1347
|
+
try {
|
|
1348
|
+
listener();
|
|
1349
|
+
} catch (error) {
|
|
1350
|
+
reportCallbackError(label2, error);
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1185
1355
|
// src/data/retry.ts
|
|
1186
1356
|
function calculateDelay(attempt, strategy, baseDelay, maxDelay, jitter) {
|
|
1187
1357
|
let delay;
|
|
@@ -1214,39 +1384,54 @@ async function withRetry(fn, options, onRetry, signal2) {
|
|
|
1214
1384
|
const shouldRetry = options?.shouldRetry ?? (() => true);
|
|
1215
1385
|
let lastError;
|
|
1216
1386
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1217
|
-
if (signal2?.aborted) throw
|
|
1387
|
+
if (signal2?.aborted) throw createAbortError();
|
|
1218
1388
|
try {
|
|
1219
|
-
if (signal2?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
1220
1389
|
return await fn();
|
|
1221
1390
|
} catch (error) {
|
|
1222
1391
|
lastError = error;
|
|
1392
|
+
if (signal2?.aborted) throw createAbortError();
|
|
1393
|
+
if (isAbortError(error)) throw error;
|
|
1223
1394
|
if (attempt >= maxRetries || !shouldRetry(error, attempt)) throw error;
|
|
1224
1395
|
const delay = calculateDelay(attempt, strategy, baseDelay, maxDelay, jitter);
|
|
1225
1396
|
onRetry?.(error, attempt, delay);
|
|
1226
|
-
await
|
|
1227
|
-
let onAbort = null;
|
|
1228
|
-
const timer = setTimeout(() => {
|
|
1229
|
-
if (onAbort && signal2) signal2.removeEventListener("abort", onAbort);
|
|
1230
|
-
resolve();
|
|
1231
|
-
}, delay);
|
|
1232
|
-
if (signal2) {
|
|
1233
|
-
onAbort = () => {
|
|
1234
|
-
clearTimeout(timer);
|
|
1235
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
1236
|
-
};
|
|
1237
|
-
signal2.addEventListener("abort", onAbort, { once: true });
|
|
1238
|
-
}
|
|
1239
|
-
});
|
|
1397
|
+
await waitForRetryDelay(delay, signal2);
|
|
1240
1398
|
}
|
|
1241
1399
|
}
|
|
1242
1400
|
throw lastError;
|
|
1243
1401
|
}
|
|
1402
|
+
function waitForRetryDelay(delay, signal2) {
|
|
1403
|
+
if (signal2?.aborted) return Promise.reject(createAbortError());
|
|
1404
|
+
return new Promise((resolve, reject) => {
|
|
1405
|
+
let settled = false;
|
|
1406
|
+
const finish = (action) => {
|
|
1407
|
+
if (settled) return;
|
|
1408
|
+
settled = true;
|
|
1409
|
+
clearTimeout(timer);
|
|
1410
|
+
if (signal2) signal2.removeEventListener("abort", onAbort);
|
|
1411
|
+
action();
|
|
1412
|
+
};
|
|
1413
|
+
const onAbort = () => finish(() => reject(createAbortError()));
|
|
1414
|
+
const timer = setTimeout(() => finish(resolve), delay);
|
|
1415
|
+
if (signal2) {
|
|
1416
|
+
signal2.addEventListener("abort", onAbort, { once: true });
|
|
1417
|
+
if (signal2.aborted) onAbort();
|
|
1418
|
+
}
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1244
1421
|
|
|
1245
1422
|
// src/data/query.ts
|
|
1246
1423
|
var globalQueryCache = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.query.cache.v1"), () => /* @__PURE__ */ new Map());
|
|
1247
1424
|
function getActiveQueryCache() {
|
|
1248
1425
|
return getRequestScopedCache("query") ?? globalQueryCache;
|
|
1249
1426
|
}
|
|
1427
|
+
function abandonEntry(entry) {
|
|
1428
|
+
if (entry.gcTimer) clearTimeout(entry.gcTimer);
|
|
1429
|
+
entry.gcTimer = null;
|
|
1430
|
+
entry.controller?.abort();
|
|
1431
|
+
entry.controller = null;
|
|
1432
|
+
entry.promise = null;
|
|
1433
|
+
entry.generation++;
|
|
1434
|
+
}
|
|
1250
1435
|
function getOrCreateEntry(cache, key, initialData) {
|
|
1251
1436
|
let entry = cache.get(key);
|
|
1252
1437
|
if (!entry) {
|
|
@@ -1258,7 +1443,9 @@ function getOrCreateEntry(cache, key, initialData) {
|
|
|
1258
1443
|
gcTimer: null,
|
|
1259
1444
|
promise: null,
|
|
1260
1445
|
listeners: /* @__PURE__ */ new Set(),
|
|
1261
|
-
refetchers: /* @__PURE__ */ new Set()
|
|
1446
|
+
refetchers: /* @__PURE__ */ new Set(),
|
|
1447
|
+
controller: null,
|
|
1448
|
+
generation: 0
|
|
1262
1449
|
};
|
|
1263
1450
|
cache.set(key, entry);
|
|
1264
1451
|
}
|
|
@@ -1284,9 +1471,10 @@ function query(key, fetcher, options = {}) {
|
|
|
1284
1471
|
const [data2, setData] = signal(initialData);
|
|
1285
1472
|
const [isFetching, setIsFetching] = signal(false);
|
|
1286
1473
|
const [error, setError] = signal(void 0);
|
|
1287
|
-
let abortController = null;
|
|
1288
1474
|
let disposed = false;
|
|
1289
1475
|
let currentKey = null;
|
|
1476
|
+
let attachedEntry = null;
|
|
1477
|
+
let attachedKey = null;
|
|
1290
1478
|
let intervalTimer = null;
|
|
1291
1479
|
const loading = derived(() => isFetching() && data2() === void 0);
|
|
1292
1480
|
const isStale = derived(() => {
|
|
@@ -1296,82 +1484,128 @@ function query(key, fetcher, options = {}) {
|
|
|
1296
1484
|
if (!entry || entry.dataUpdatedAt === 0) return true;
|
|
1297
1485
|
return Date.now() - entry.dataUpdatedAt >= staleTime;
|
|
1298
1486
|
});
|
|
1487
|
+
function detachFromEntry() {
|
|
1488
|
+
const entry = attachedEntry;
|
|
1489
|
+
const key2 = attachedKey;
|
|
1490
|
+
if (!entry) return;
|
|
1491
|
+
attachedEntry = null;
|
|
1492
|
+
attachedKey = null;
|
|
1493
|
+
entry.listeners.delete(onCacheUpdate);
|
|
1494
|
+
entry.refetchers.delete(doFetch);
|
|
1495
|
+
entry.subscribers = Math.max(0, entry.subscribers - 1);
|
|
1496
|
+
if (entry.subscribers > 0 || cacheTime < 0 || key2 === null) return;
|
|
1497
|
+
if (cache.get(key2) !== entry) return;
|
|
1498
|
+
if (entry.gcTimer !== null) clearTimeout(entry.gcTimer);
|
|
1499
|
+
entry.gcTimer = setTimeout(() => {
|
|
1500
|
+
const current = cache.get(key2);
|
|
1501
|
+
if (current === entry && current.subscribers <= 0) {
|
|
1502
|
+
abandonEntry(current);
|
|
1503
|
+
cache.delete(key2);
|
|
1504
|
+
}
|
|
1505
|
+
}, cacheTime);
|
|
1506
|
+
entry.gcTimer.unref?.();
|
|
1507
|
+
}
|
|
1508
|
+
function attachToEntry(entry, key2) {
|
|
1509
|
+
if (attachedEntry === entry) {
|
|
1510
|
+
if (entry.gcTimer !== null) {
|
|
1511
|
+
clearTimeout(entry.gcTimer);
|
|
1512
|
+
entry.gcTimer = null;
|
|
1513
|
+
}
|
|
1514
|
+
return;
|
|
1515
|
+
}
|
|
1516
|
+
detachFromEntry();
|
|
1517
|
+
attachedEntry = entry;
|
|
1518
|
+
attachedKey = key2;
|
|
1519
|
+
entry.subscribers++;
|
|
1520
|
+
entry.listeners.add(onCacheUpdate);
|
|
1521
|
+
entry.refetchers.add(doFetch);
|
|
1522
|
+
if (entry.gcTimer !== null) {
|
|
1523
|
+
clearTimeout(entry.gcTimer);
|
|
1524
|
+
entry.gcTimer = null;
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1299
1527
|
async function doFetch() {
|
|
1300
1528
|
if (disposed || !currentKey || !enabled) return;
|
|
1301
1529
|
const key2 = currentKey;
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
entry = getOrCreateEntry(cache, key2);
|
|
1305
|
-
entry.listeners.add(onCacheUpdate);
|
|
1306
|
-
entry.refetchers.add(doFetch);
|
|
1307
|
-
}
|
|
1530
|
+
const entry = getOrCreateEntry(cache, key2);
|
|
1531
|
+
attachToEntry(entry, key2);
|
|
1308
1532
|
if (entry.promise) {
|
|
1309
1533
|
setIsFetching(true);
|
|
1310
1534
|
const captured = entry.promise;
|
|
1311
1535
|
try {
|
|
1312
1536
|
await captured;
|
|
1313
|
-
if (disposed || currentKey !== key2) return;
|
|
1314
|
-
if (entry.promise === captured) {
|
|
1315
|
-
onCacheUpdate();
|
|
1316
|
-
if (entry.error) onError?.(entry.error);
|
|
1317
|
-
else if (entry.data !== void 0) onSuccess?.(entry.data);
|
|
1318
|
-
}
|
|
1319
1537
|
} catch {
|
|
1320
|
-
|
|
1321
|
-
if (
|
|
1538
|
+
} finally {
|
|
1539
|
+
if (!disposed && currentKey === key2) {
|
|
1322
1540
|
onCacheUpdate();
|
|
1323
|
-
if (entry.error)
|
|
1541
|
+
if (entry.error) {
|
|
1542
|
+
const settledError = entry.error;
|
|
1543
|
+
runCallback("query onError", () => onError?.(settledError));
|
|
1544
|
+
} else if (entry.data !== void 0) {
|
|
1545
|
+
const settledData = entry.data;
|
|
1546
|
+
runCallback("query onSuccess", () => onSuccess?.(settledData));
|
|
1547
|
+
}
|
|
1548
|
+
runCallback("query onSettled", () => onSettled?.());
|
|
1324
1549
|
}
|
|
1325
|
-
} finally {
|
|
1326
|
-
if (!disposed && currentKey === key2) onSettled?.();
|
|
1327
1550
|
}
|
|
1328
1551
|
return;
|
|
1329
1552
|
}
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
const
|
|
1553
|
+
entry.controller = new AbortController();
|
|
1554
|
+
const signal2 = entry.controller.signal;
|
|
1555
|
+
const generation = ++entry.generation;
|
|
1333
1556
|
setIsFetching(true);
|
|
1334
1557
|
let promise;
|
|
1335
1558
|
try {
|
|
1336
1559
|
promise = withRetry(() => fetcher({ signal: signal2, key: key2 }), retryOptions, void 0, signal2);
|
|
1337
1560
|
} catch (err) {
|
|
1338
1561
|
setIsFetching(false);
|
|
1562
|
+
if (isAbortError(err)) return;
|
|
1339
1563
|
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
1340
1564
|
entry.error = errorObj;
|
|
1341
|
-
onError?.(errorObj);
|
|
1342
|
-
onSettled?.();
|
|
1565
|
+
runCallback("query onError", () => onError?.(errorObj));
|
|
1566
|
+
runCallback("query onSettled", () => onSettled?.());
|
|
1343
1567
|
return;
|
|
1344
1568
|
}
|
|
1345
1569
|
entry.promise = promise;
|
|
1346
1570
|
try {
|
|
1347
1571
|
const result = await promise;
|
|
1572
|
+
if (entry.generation !== generation) return;
|
|
1348
1573
|
entry.promise = null;
|
|
1349
|
-
|
|
1574
|
+
entry.controller = null;
|
|
1350
1575
|
entry.data = result;
|
|
1351
1576
|
entry.dataUpdatedAt = Date.now();
|
|
1352
1577
|
entry.error = void 0;
|
|
1353
|
-
|
|
1578
|
+
notifyListeners(entry.listeners, "query cache listener");
|
|
1579
|
+
if (disposed || currentKey !== key2) return;
|
|
1580
|
+
const selected = runSelect("query select", () => select3 ? select3(result) : result);
|
|
1354
1581
|
batch(() => {
|
|
1355
|
-
setData(selected);
|
|
1582
|
+
if (selected.ok) setData(selected.value);
|
|
1356
1583
|
setIsFetching(false);
|
|
1357
1584
|
setError(void 0);
|
|
1358
1585
|
});
|
|
1359
|
-
|
|
1360
|
-
onSuccess?.(result);
|
|
1586
|
+
runCallback("query onSuccess", () => onSuccess?.(result));
|
|
1361
1587
|
} catch (err) {
|
|
1588
|
+
if (entry.generation !== generation) return;
|
|
1362
1589
|
entry.promise = null;
|
|
1363
|
-
|
|
1364
|
-
if (err
|
|
1590
|
+
entry.controller = null;
|
|
1591
|
+
if (isAbortError(err)) {
|
|
1592
|
+
notifyListeners(entry.listeners, "query cache listener");
|
|
1593
|
+
if (!disposed && currentKey === key2) setIsFetching(false);
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1365
1596
|
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
1366
1597
|
entry.error = errorObj;
|
|
1598
|
+
notifyListeners(entry.listeners, "query cache listener");
|
|
1599
|
+
if (disposed || currentKey !== key2) return;
|
|
1367
1600
|
batch(() => {
|
|
1368
1601
|
setError(errorObj);
|
|
1369
1602
|
setIsFetching(false);
|
|
1370
1603
|
});
|
|
1371
|
-
|
|
1372
|
-
onError?.(errorObj);
|
|
1604
|
+
runCallback("query onError", () => onError?.(errorObj));
|
|
1373
1605
|
} finally {
|
|
1374
|
-
if (!disposed && currentKey === key2)
|
|
1606
|
+
if (!disposed && currentKey === key2 && entry.generation === generation) {
|
|
1607
|
+
runCallback("query onSettled", () => onSettled?.());
|
|
1608
|
+
}
|
|
1375
1609
|
}
|
|
1376
1610
|
}
|
|
1377
1611
|
function onCacheUpdate() {
|
|
@@ -1386,43 +1620,24 @@ function query(key, fetcher, options = {}) {
|
|
|
1386
1620
|
return;
|
|
1387
1621
|
}
|
|
1388
1622
|
const raw = entry.data;
|
|
1389
|
-
const selected = raw !== void 0 && select3 ? select3(raw) : raw;
|
|
1623
|
+
const selected = raw !== void 0 && select3 ? runSelect("query select", () => select3(raw)) : { ok: true, value: raw };
|
|
1390
1624
|
batch(() => {
|
|
1391
|
-
setData(selected);
|
|
1625
|
+
if (selected.ok) setData(selected.value);
|
|
1392
1626
|
setError(entry.error);
|
|
1393
1627
|
if (!entry.promise) setIsFetching(false);
|
|
1394
1628
|
});
|
|
1395
1629
|
}
|
|
1396
1630
|
const effectCleanup = effect(() => {
|
|
1397
1631
|
const key2 = resolveKey();
|
|
1398
|
-
if (currentKey !== null && currentKey !== key2) {
|
|
1399
|
-
const oldEntry = cache.get(currentKey);
|
|
1400
|
-
if (oldEntry) {
|
|
1401
|
-
oldEntry.listeners.delete(onCacheUpdate);
|
|
1402
|
-
oldEntry.refetchers.delete(doFetch);
|
|
1403
|
-
oldEntry.subscribers--;
|
|
1404
|
-
if (oldEntry.subscribers <= 0 && cacheTime >= 0) {
|
|
1405
|
-
const oldKey = currentKey;
|
|
1406
|
-
if (oldEntry.gcTimer !== null) clearTimeout(oldEntry.gcTimer);
|
|
1407
|
-
oldEntry.gcTimer = setTimeout(() => cache.delete(oldKey), cacheTime);
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
}
|
|
1411
1632
|
const keyChanged = currentKey !== key2;
|
|
1412
1633
|
currentKey = key2;
|
|
1413
1634
|
const entry = getOrCreateEntry(cache, key2, initialData);
|
|
1414
|
-
|
|
1415
|
-
if (entry.gcTimer !== null) {
|
|
1416
|
-
clearTimeout(entry.gcTimer);
|
|
1417
|
-
entry.gcTimer = null;
|
|
1418
|
-
}
|
|
1419
|
-
entry.listeners.add(onCacheUpdate);
|
|
1420
|
-
entry.refetchers.add(doFetch);
|
|
1635
|
+
attachToEntry(entry, key2);
|
|
1421
1636
|
if (entry.data !== void 0) {
|
|
1422
1637
|
const raw = entry.data;
|
|
1423
|
-
const selected = select3 ? select3(raw) : raw;
|
|
1638
|
+
const selected = runSelect("query select", () => select3 ? select3(raw) : raw);
|
|
1424
1639
|
batch(() => {
|
|
1425
|
-
setData(selected);
|
|
1640
|
+
if (selected.ok) setData(selected.value);
|
|
1426
1641
|
setError(entry.error);
|
|
1427
1642
|
});
|
|
1428
1643
|
}
|
|
@@ -1460,22 +1675,9 @@ function query(key, fetcher, options = {}) {
|
|
|
1460
1675
|
function dispose2() {
|
|
1461
1676
|
if (disposed) return;
|
|
1462
1677
|
disposed = true;
|
|
1463
|
-
abortController?.abort();
|
|
1464
1678
|
effectCleanup();
|
|
1465
1679
|
if (intervalTimer) clearInterval(intervalTimer);
|
|
1466
|
-
|
|
1467
|
-
const entry = cache.get(currentKey);
|
|
1468
|
-
if (entry) {
|
|
1469
|
-
entry.listeners.delete(onCacheUpdate);
|
|
1470
|
-
entry.refetchers.delete(doFetch);
|
|
1471
|
-
entry.subscribers--;
|
|
1472
|
-
if (entry.subscribers <= 0 && cacheTime >= 0) {
|
|
1473
|
-
const key2 = currentKey;
|
|
1474
|
-
if (entry.gcTimer !== null) clearTimeout(entry.gcTimer);
|
|
1475
|
-
entry.gcTimer = setTimeout(() => cache.delete(key2), cacheTime);
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
}
|
|
1680
|
+
detachFromEntry();
|
|
1479
1681
|
if (focusHandler && typeof globalThis.removeEventListener === "function") {
|
|
1480
1682
|
globalThis.removeEventListener("focus", focusHandler);
|
|
1481
1683
|
}
|
|
@@ -1511,21 +1713,21 @@ function setQueryData(key, data2) {
|
|
|
1511
1713
|
const newData = typeof data2 === "function" ? data2(entry.data) : data2;
|
|
1512
1714
|
entry.data = newData;
|
|
1513
1715
|
entry.dataUpdatedAt = Date.now();
|
|
1514
|
-
|
|
1716
|
+
notifyListeners(entry.listeners, "query cache listener");
|
|
1515
1717
|
}
|
|
1516
1718
|
function clearQueryCache() {
|
|
1517
1719
|
const activeListeners = [];
|
|
1518
1720
|
const activeRefetchers = [];
|
|
1519
1721
|
const activeCache = getActiveQueryCache();
|
|
1520
1722
|
for (const entry of activeCache.values()) {
|
|
1521
|
-
if (entry.gcTimer) clearTimeout(entry.gcTimer);
|
|
1522
1723
|
if (entry.subscribers > 0) {
|
|
1523
1724
|
for (const listener of entry.listeners) activeListeners.push(listener);
|
|
1524
1725
|
for (const refetcher of entry.refetchers) activeRefetchers.push(refetcher);
|
|
1525
1726
|
}
|
|
1727
|
+
abandonEntry(entry);
|
|
1526
1728
|
}
|
|
1527
1729
|
activeCache.clear();
|
|
1528
|
-
|
|
1730
|
+
notifyListeners(activeListeners, "query cache listener");
|
|
1529
1731
|
for (const refetcher of activeRefetchers) {
|
|
1530
1732
|
refetcher().catch((err) => {
|
|
1531
1733
|
if (typeof console !== "undefined") {
|
|
@@ -1537,7 +1739,7 @@ function clearQueryCache() {
|
|
|
1537
1739
|
function __resetQueryCache() {
|
|
1538
1740
|
const activeCache = getActiveQueryCache();
|
|
1539
1741
|
for (const entry of activeCache.values()) {
|
|
1540
|
-
|
|
1742
|
+
abandonEntry(entry);
|
|
1541
1743
|
}
|
|
1542
1744
|
activeCache.clear();
|
|
1543
1745
|
}
|
|
@@ -1552,6 +1754,8 @@ function mutation(mutationFn, options = {}) {
|
|
|
1552
1754
|
const isIdle = derived(() => status() === "idle");
|
|
1553
1755
|
let runId = 0;
|
|
1554
1756
|
let abortController = null;
|
|
1757
|
+
let terminalStatus = "idle";
|
|
1758
|
+
let terminalError;
|
|
1555
1759
|
async function execute(variables) {
|
|
1556
1760
|
abortController?.abort();
|
|
1557
1761
|
abortController = new AbortController();
|
|
@@ -1569,25 +1773,38 @@ function mutation(mutationFn, options = {}) {
|
|
|
1569
1773
|
}
|
|
1570
1774
|
const result = await withRetry(() => mutationFn(variables, signal2), options.retry, void 0, signal2);
|
|
1571
1775
|
if (myRun !== runId) return result;
|
|
1776
|
+
terminalStatus = "success";
|
|
1777
|
+
terminalError = void 0;
|
|
1572
1778
|
batch(() => {
|
|
1573
1779
|
setData(result);
|
|
1574
1780
|
setLoading(false);
|
|
1575
1781
|
setStatus("success");
|
|
1576
1782
|
});
|
|
1577
|
-
options.onSuccess?.(result, variables, context2);
|
|
1578
|
-
options.onSettled?.(result, void 0, variables, context2);
|
|
1783
|
+
runCallback("mutation onSuccess", () => options.onSuccess?.(result, variables, context2));
|
|
1784
|
+
runCallback("mutation onSettled", () => options.onSettled?.(result, void 0, variables, context2));
|
|
1579
1785
|
return result;
|
|
1580
1786
|
} catch (err) {
|
|
1787
|
+
if (isAbortError(err)) {
|
|
1788
|
+
if (myRun === runId) {
|
|
1789
|
+
batch(() => {
|
|
1790
|
+
setLoading(false);
|
|
1791
|
+
setError(terminalError);
|
|
1792
|
+
setStatus(terminalStatus);
|
|
1793
|
+
});
|
|
1794
|
+
}
|
|
1795
|
+
throw err;
|
|
1796
|
+
}
|
|
1581
1797
|
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
1582
|
-
if (errorObj instanceof DOMException && errorObj.name === "AbortError") throw errorObj;
|
|
1583
1798
|
if (myRun !== runId) throw errorObj;
|
|
1799
|
+
terminalStatus = "error";
|
|
1800
|
+
terminalError = errorObj;
|
|
1584
1801
|
batch(() => {
|
|
1585
1802
|
setError(errorObj);
|
|
1586
1803
|
setLoading(false);
|
|
1587
1804
|
setStatus("error");
|
|
1588
1805
|
});
|
|
1589
|
-
options.onError?.(errorObj, variables, context2);
|
|
1590
|
-
options.onSettled?.(void 0, errorObj, variables, context2);
|
|
1806
|
+
runCallback("mutation onError", () => options.onError?.(errorObj, variables, context2));
|
|
1807
|
+
runCallback("mutation onSettled", () => options.onSettled?.(void 0, errorObj, variables, context2));
|
|
1591
1808
|
throw errorObj;
|
|
1592
1809
|
}
|
|
1593
1810
|
}
|
|
@@ -1595,6 +1812,8 @@ function mutation(mutationFn, options = {}) {
|
|
|
1595
1812
|
runId++;
|
|
1596
1813
|
abortController?.abort();
|
|
1597
1814
|
abortController = null;
|
|
1815
|
+
terminalStatus = "idle";
|
|
1816
|
+
terminalError = void 0;
|
|
1598
1817
|
batch(() => {
|
|
1599
1818
|
setData(void 0);
|
|
1600
1819
|
setError(void 0);
|
|
@@ -1610,7 +1829,7 @@ function mutation(mutationFn, options = {}) {
|
|
|
1610
1829
|
isIdle,
|
|
1611
1830
|
mutate: (variables) => {
|
|
1612
1831
|
execute(variables).catch((err) => {
|
|
1613
|
-
if (err
|
|
1832
|
+
if (isAbortError(err)) return;
|
|
1614
1833
|
if (typeof console !== "undefined") {
|
|
1615
1834
|
console.warn("[SibuJS mutation] mutate() failed; check `.error()` signal or onError option.", err);
|
|
1616
1835
|
}
|
|
@@ -1683,10 +1902,17 @@ function infiniteQuery(key, fetcher, options) {
|
|
|
1683
1902
|
setIsFetchingNext(false);
|
|
1684
1903
|
setIsFetchingPrev(false);
|
|
1685
1904
|
});
|
|
1686
|
-
onSuccess?.(newPages);
|
|
1905
|
+
runCallback("infiniteQuery onSuccess", () => onSuccess?.(newPages));
|
|
1687
1906
|
} catch (err) {
|
|
1688
1907
|
if (disposed || myRun !== runId) return;
|
|
1689
|
-
if (err
|
|
1908
|
+
if (isAbortError(err)) {
|
|
1909
|
+
batch(() => {
|
|
1910
|
+
setIsFetching(false);
|
|
1911
|
+
setIsFetchingNext(false);
|
|
1912
|
+
setIsFetchingPrev(false);
|
|
1913
|
+
});
|
|
1914
|
+
return;
|
|
1915
|
+
}
|
|
1690
1916
|
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
1691
1917
|
batch(() => {
|
|
1692
1918
|
setError(errorObj);
|
|
@@ -1694,7 +1920,7 @@ function infiniteQuery(key, fetcher, options) {
|
|
|
1694
1920
|
setIsFetchingNext(false);
|
|
1695
1921
|
setIsFetchingPrev(false);
|
|
1696
1922
|
});
|
|
1697
|
-
onError?.(errorObj);
|
|
1923
|
+
runCallback("infiniteQuery onError", () => onError?.(errorObj));
|
|
1698
1924
|
}
|
|
1699
1925
|
})();
|
|
1700
1926
|
inFlight = promise;
|
|
@@ -1876,7 +2102,7 @@ function resource(sourceOrFetcher, fetcherOrOptions, maybeOptions) {
|
|
|
1876
2102
|
setLoading(true);
|
|
1877
2103
|
setError(void 0);
|
|
1878
2104
|
});
|
|
1879
|
-
options.onStart?.();
|
|
2105
|
+
runCallback("resource onStart", () => options.onStart?.());
|
|
1880
2106
|
try {
|
|
1881
2107
|
const result = await withRetry(() => fetcher(sourceValue, { signal: signal2, prev }), options.retry, void 0, signal2);
|
|
1882
2108
|
if (version !== fetchVersion || disposed) return;
|
|
@@ -1885,10 +2111,10 @@ function resource(sourceOrFetcher, fetcherOrOptions, maybeOptions) {
|
|
|
1885
2111
|
setData(result);
|
|
1886
2112
|
setLoading(false);
|
|
1887
2113
|
});
|
|
1888
|
-
options.onSuccess?.(result);
|
|
2114
|
+
runCallback("resource onSuccess", () => options.onSuccess?.(result));
|
|
1889
2115
|
} catch (err) {
|
|
1890
2116
|
if (version !== fetchVersion || disposed) return;
|
|
1891
|
-
if (err
|
|
2117
|
+
if (isAbortError(err)) {
|
|
1892
2118
|
if (version === fetchVersion) setLoading(false);
|
|
1893
2119
|
return;
|
|
1894
2120
|
}
|
|
@@ -1897,10 +2123,10 @@ function resource(sourceOrFetcher, fetcherOrOptions, maybeOptions) {
|
|
|
1897
2123
|
setError(errorObj);
|
|
1898
2124
|
setLoading(false);
|
|
1899
2125
|
});
|
|
1900
|
-
options.onError?.(errorObj);
|
|
2126
|
+
runCallback("resource onError", () => options.onError?.(errorObj));
|
|
1901
2127
|
} finally {
|
|
1902
2128
|
if (version === fetchVersion) {
|
|
1903
|
-
options.onSettled?.();
|
|
2129
|
+
runCallback("resource onSettled", () => options.onSettled?.());
|
|
1904
2130
|
}
|
|
1905
2131
|
}
|
|
1906
2132
|
}
|
|
@@ -2088,13 +2314,14 @@ async function offlineStore(options) {
|
|
|
2088
2314
|
return data2().filter(filter);
|
|
2089
2315
|
}
|
|
2090
2316
|
async function sync() {
|
|
2091
|
-
|
|
2317
|
+
const syncAdapter2 = adapter;
|
|
2318
|
+
if (!syncAdapter2 || isSyncing() || closed) return;
|
|
2092
2319
|
setIsSyncing(true);
|
|
2093
2320
|
try {
|
|
2094
2321
|
const snapshot = await idbGetAllWithKeys(db, "_changes");
|
|
2095
2322
|
if (closed) return;
|
|
2096
2323
|
if (snapshot.length > 0) {
|
|
2097
|
-
const result = await
|
|
2324
|
+
const result = await syncAdapter2.push(snapshot.map((e) => e.value));
|
|
2098
2325
|
if (closed) return;
|
|
2099
2326
|
if (result.ok) {
|
|
2100
2327
|
await idbDeleteKeys(
|
|
@@ -2107,20 +2334,18 @@ async function offlineStore(options) {
|
|
|
2107
2334
|
console.warn(`[offlineStore] push rejected by adapter${result.error ? `: ${result.error}` : ""}`);
|
|
2108
2335
|
}
|
|
2109
2336
|
}
|
|
2110
|
-
const remoteItems = await
|
|
2337
|
+
const remoteItems = await syncAdapter2.pull(lastSynced());
|
|
2111
2338
|
if (closed) return;
|
|
2112
2339
|
const pendingChanges = await idbGetAll(db, "_changes");
|
|
2113
2340
|
if (closed) return;
|
|
2114
|
-
const
|
|
2341
|
+
const pendingByKey = /* @__PURE__ */ new Map();
|
|
2115
2342
|
for (const c of pendingChanges) {
|
|
2116
2343
|
const k = c.item[keyPath];
|
|
2117
|
-
if (k != null)
|
|
2344
|
+
if (k != null) pendingByKey.set(k, c);
|
|
2118
2345
|
}
|
|
2119
|
-
const
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
});
|
|
2123
|
-
await idbPutMany(db, "items", safeRemote);
|
|
2346
|
+
const resolved = await resolveRemoteItems(syncAdapter2, remoteItems, pendingByKey);
|
|
2347
|
+
if (closed) return;
|
|
2348
|
+
await idbPutMany(db, "items", resolved);
|
|
2124
2349
|
if (closed) return;
|
|
2125
2350
|
const now = Date.now();
|
|
2126
2351
|
await idbPut(db, "_meta", now, "lastSynced");
|
|
@@ -2133,6 +2358,42 @@ async function offlineStore(options) {
|
|
|
2133
2358
|
setIsSyncing(false);
|
|
2134
2359
|
}
|
|
2135
2360
|
}
|
|
2361
|
+
async function resolveRemoteItems(activeAdapter, remoteItems, pendingByKey) {
|
|
2362
|
+
const strategy = activeAdapter.conflictStrategy ?? "client-wins";
|
|
2363
|
+
const out = [];
|
|
2364
|
+
for (const remote of remoteItems) {
|
|
2365
|
+
const key = remote[keyPath];
|
|
2366
|
+
const pending = key == null ? void 0 : pendingByKey.get(key);
|
|
2367
|
+
if (!pending) {
|
|
2368
|
+
out.push(remote);
|
|
2369
|
+
continue;
|
|
2370
|
+
}
|
|
2371
|
+
if (strategy === "server-wins") {
|
|
2372
|
+
out.push(remote);
|
|
2373
|
+
continue;
|
|
2374
|
+
}
|
|
2375
|
+
if (strategy === "manual") {
|
|
2376
|
+
if (!activeAdapter.resolveConflict) {
|
|
2377
|
+
if (typeof console !== "undefined") {
|
|
2378
|
+
console.warn(
|
|
2379
|
+
'[offlineStore] conflictStrategy is "manual" but the adapter has no resolveConflict(); keeping the local record for this key (client-wins).'
|
|
2380
|
+
);
|
|
2381
|
+
}
|
|
2382
|
+
continue;
|
|
2383
|
+
}
|
|
2384
|
+
try {
|
|
2385
|
+
const local = pending.type === "delete" ? void 0 : await idbGet(db, "items", key);
|
|
2386
|
+
const choice = await activeAdapter.resolveConflict({ key, local, remote, pending });
|
|
2387
|
+
if (choice !== void 0) out.push(choice);
|
|
2388
|
+
} catch (err) {
|
|
2389
|
+
if (typeof console !== "undefined") {
|
|
2390
|
+
console.warn("[offlineStore] resolveConflict threw; keeping the local record", err);
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
return out;
|
|
2396
|
+
}
|
|
2136
2397
|
function attach(newAdapter) {
|
|
2137
2398
|
adapter = newAdapter;
|
|
2138
2399
|
}
|
|
@@ -2171,10 +2432,18 @@ function syncAdapter(config) {
|
|
|
2171
2432
|
}
|
|
2172
2433
|
|
|
2173
2434
|
// src/core/rendering/context.ts
|
|
2435
|
+
var _isDev4 = isDev();
|
|
2174
2436
|
function context(defaultValue) {
|
|
2175
2437
|
const [getValue, setValue] = signal(defaultValue);
|
|
2438
|
+
const warnIfSSR = (method) => {
|
|
2439
|
+
if (!_isDev4 || !isSSR()) return;
|
|
2440
|
+
devWarn(
|
|
2441
|
+
`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.`
|
|
2442
|
+
);
|
|
2443
|
+
};
|
|
2176
2444
|
const ctx = {
|
|
2177
2445
|
provide(value) {
|
|
2446
|
+
warnIfSSR("provide");
|
|
2178
2447
|
const previous2 = getValue();
|
|
2179
2448
|
setValue(value);
|
|
2180
2449
|
return () => setValue(previous2);
|
|
@@ -2186,13 +2455,21 @@ function context(defaultValue) {
|
|
|
2186
2455
|
return getValue();
|
|
2187
2456
|
},
|
|
2188
2457
|
set(value) {
|
|
2458
|
+
warnIfSSR("set");
|
|
2189
2459
|
setValue(value);
|
|
2190
2460
|
},
|
|
2191
2461
|
withContext(value, fn) {
|
|
2462
|
+
warnIfSSR("withContext");
|
|
2192
2463
|
const previous2 = getValue();
|
|
2193
2464
|
setValue(value);
|
|
2194
2465
|
try {
|
|
2195
|
-
|
|
2466
|
+
const result = fn();
|
|
2467
|
+
if (_isDev4 && result !== null && typeof result?.then === "function") {
|
|
2468
|
+
devWarn(
|
|
2469
|
+
"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."
|
|
2470
|
+
);
|
|
2471
|
+
}
|
|
2472
|
+
return result;
|
|
2196
2473
|
} finally {
|
|
2197
2474
|
setValue(previous2);
|
|
2198
2475
|
}
|
|
@@ -2348,16 +2625,21 @@ function socket(url, options) {
|
|
|
2348
2625
|
function stripControlChars(value) {
|
|
2349
2626
|
return value.replace(/[\x00-\x20\x7f-\x9f]+/g, "");
|
|
2350
2627
|
}
|
|
2628
|
+
function canonicalAttrName(name) {
|
|
2629
|
+
return name.replace(/[A-Z]/g, (ch) => String.fromCharCode(ch.charCodeAt(0) + 32));
|
|
2630
|
+
}
|
|
2351
2631
|
function isEventHandlerAttr(name) {
|
|
2352
2632
|
if (name.length < 3) return false;
|
|
2353
2633
|
const lower = name.toLowerCase();
|
|
2354
2634
|
return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
|
|
2355
2635
|
}
|
|
2356
2636
|
var SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
|
|
2637
|
+
var OUTER_TRIM = /^[\x00-\x20\x7f-\x9f]+|[\x00-\x20\x7f-\x9f]+$/g;
|
|
2357
2638
|
function sanitizeUrl(url) {
|
|
2358
|
-
const
|
|
2359
|
-
if (!
|
|
2360
|
-
const
|
|
2639
|
+
const probe = stripControlChars(url).trim();
|
|
2640
|
+
if (!probe) return "";
|
|
2641
|
+
const output2 = url.replace(OUTER_TRIM, "");
|
|
2642
|
+
const lower = probe.toLowerCase();
|
|
2361
2643
|
let schemeEnd = -1;
|
|
2362
2644
|
for (let i2 = 0; i2 < lower.length; i2++) {
|
|
2363
2645
|
const ch = lower.charCodeAt(i2);
|
|
@@ -2367,12 +2649,13 @@ function sanitizeUrl(url) {
|
|
|
2367
2649
|
}
|
|
2368
2650
|
if (ch === 47 || ch === 63 || ch === 35) break;
|
|
2369
2651
|
}
|
|
2370
|
-
if (schemeEnd === -1) return
|
|
2652
|
+
if (schemeEnd === -1) return output2;
|
|
2371
2653
|
const scheme = lower.slice(0, schemeEnd + 1);
|
|
2372
|
-
if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return
|
|
2654
|
+
if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return output2;
|
|
2373
2655
|
if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
|
|
2374
|
-
return
|
|
2656
|
+
return output2;
|
|
2375
2657
|
}
|
|
2658
|
+
var SRCSET_DESCRIPTOR = /^\s+\d+(\.\d+)?[wx]$/;
|
|
2376
2659
|
function sanitizeSrcset(value) {
|
|
2377
2660
|
const parts = value.split(",");
|
|
2378
2661
|
const out = [];
|
|
@@ -2381,6 +2664,7 @@ function sanitizeSrcset(value) {
|
|
|
2381
2664
|
if (!part) continue;
|
|
2382
2665
|
const m = part.match(/^(\S+)(\s+.+)?$/);
|
|
2383
2666
|
if (!m) continue;
|
|
2667
|
+
if (m[2] !== void 0 && !SRCSET_DESCRIPTOR.test(m[2])) continue;
|
|
2384
2668
|
const safe = sanitizeUrl(m[1]);
|
|
2385
2669
|
if (!safe) continue;
|
|
2386
2670
|
out.push(m[2] ? `${safe}${m[2]}` : safe);
|
|
@@ -2388,23 +2672,83 @@ function sanitizeSrcset(value) {
|
|
|
2388
2672
|
return out.join(", ");
|
|
2389
2673
|
}
|
|
2390
2674
|
var CSS_DANGER_GATE = /[(:@\\]/;
|
|
2675
|
+
function isCssWhitespace(ch) {
|
|
2676
|
+
return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
|
|
2677
|
+
}
|
|
2678
|
+
function isHexDigit(ch) {
|
|
2679
|
+
return ch >= "0" && ch <= "9" || ch >= "a" && ch <= "f" || ch >= "A" && ch <= "F";
|
|
2680
|
+
}
|
|
2681
|
+
function decodeCssEscapes(value) {
|
|
2682
|
+
let out = "";
|
|
2683
|
+
for (let i2 = 0; i2 < value.length; i2++) {
|
|
2684
|
+
const ch = value[i2];
|
|
2685
|
+
if (ch !== "\\") {
|
|
2686
|
+
out += ch;
|
|
2687
|
+
continue;
|
|
2688
|
+
}
|
|
2689
|
+
if (i2 + 1 >= value.length) break;
|
|
2690
|
+
const next = value[i2 + 1];
|
|
2691
|
+
if (next === "\n" || next === "\f") {
|
|
2692
|
+
i2 += 1;
|
|
2693
|
+
continue;
|
|
2694
|
+
}
|
|
2695
|
+
if (next === "\r") {
|
|
2696
|
+
i2 += value[i2 + 2] === "\n" ? 2 : 1;
|
|
2697
|
+
continue;
|
|
2698
|
+
}
|
|
2699
|
+
if (isHexDigit(next)) {
|
|
2700
|
+
let hex = "";
|
|
2701
|
+
let j = i2 + 1;
|
|
2702
|
+
while (j < value.length && hex.length < 6 && isHexDigit(value[j])) {
|
|
2703
|
+
hex += value[j];
|
|
2704
|
+
j++;
|
|
2705
|
+
}
|
|
2706
|
+
if (j < value.length && isCssWhitespace(value[j])) {
|
|
2707
|
+
j += value[j] === "\r" && value[j + 1] === "\n" ? 2 : 1;
|
|
2708
|
+
}
|
|
2709
|
+
const code2 = Number.parseInt(hex, 16);
|
|
2710
|
+
const invalid = code2 === 0 || code2 > 1114111 || code2 >= 55296 && code2 <= 57343;
|
|
2711
|
+
out += invalid ? "\uFFFD" : String.fromCodePoint(code2);
|
|
2712
|
+
i2 = j - 1;
|
|
2713
|
+
continue;
|
|
2714
|
+
}
|
|
2715
|
+
out += next;
|
|
2716
|
+
i2 += 1;
|
|
2717
|
+
}
|
|
2718
|
+
return out;
|
|
2719
|
+
}
|
|
2391
2720
|
function sanitizeCSSValue(value) {
|
|
2392
2721
|
if (!CSS_DANGER_GATE.test(value)) return value;
|
|
2393
|
-
const
|
|
2394
|
-
|
|
2395
|
-
if (!Number.isFinite(code2) || code2 < 0 || code2 > 1114111) return "";
|
|
2396
|
-
try {
|
|
2397
|
-
return String.fromCodePoint(code2);
|
|
2398
|
-
} catch {
|
|
2399
|
-
return "";
|
|
2400
|
-
}
|
|
2401
|
-
});
|
|
2402
|
-
const lower = decoded.toLowerCase().replace(/\s+/g, "");
|
|
2722
|
+
const normalized = value.includes("\\") ? decodeCssEscapes(value) : value;
|
|
2723
|
+
const lower = normalized.toLowerCase().replace(/\s+/g, "");
|
|
2403
2724
|
if (lower.includes("url(") || lower.includes("expression(") || lower.includes("javascript:") || lower.includes("vbscript:") || lower.includes("-moz-binding") || lower.includes("behavior:") || lower.includes("@import") || lower.includes("image-set(") || lower.includes("filter:progid")) {
|
|
2404
2725
|
return "";
|
|
2405
2726
|
}
|
|
2406
2727
|
return value;
|
|
2407
2728
|
}
|
|
2729
|
+
function sanitizeStyleAttribute(cssText) {
|
|
2730
|
+
const input2 = String(cssText);
|
|
2731
|
+
if (input2.trim() === "") return "";
|
|
2732
|
+
if (typeof document === "undefined") {
|
|
2733
|
+
return sanitizeCSSValue(input2) === "" ? "" : input2;
|
|
2734
|
+
}
|
|
2735
|
+
const probe = document.createElement("div");
|
|
2736
|
+
try {
|
|
2737
|
+
probe.style.cssText = input2;
|
|
2738
|
+
} catch {
|
|
2739
|
+
return "";
|
|
2740
|
+
}
|
|
2741
|
+
const declarations = [];
|
|
2742
|
+
for (let i2 = 0; i2 < probe.style.length; i2++) {
|
|
2743
|
+
const property = probe.style[i2];
|
|
2744
|
+
const value = probe.style.getPropertyValue(property);
|
|
2745
|
+
if (sanitizeCSSValue(value) === "") continue;
|
|
2746
|
+
if (sanitizeCSSValue(`${property}:${value}`) === "") continue;
|
|
2747
|
+
const priority = probe.style.getPropertyPriority(property);
|
|
2748
|
+
declarations.push(`${property}: ${value}${priority ? ` !${priority}` : ""}`);
|
|
2749
|
+
}
|
|
2750
|
+
return declarations.join("; ");
|
|
2751
|
+
}
|
|
2408
2752
|
function stripHtml(html2) {
|
|
2409
2753
|
const input2 = String(html2);
|
|
2410
2754
|
if (typeof DOMParser !== "undefined") {
|
|
@@ -2433,12 +2777,25 @@ var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
|
2433
2777
|
"background",
|
|
2434
2778
|
"srcset",
|
|
2435
2779
|
"ping",
|
|
2436
|
-
"data"
|
|
2780
|
+
"data",
|
|
2781
|
+
// `<html manifest>` is a URL sink too. It was carried only by the SSR
|
|
2782
|
+
// serializers' private copy of this set; folding that copy into this one would
|
|
2783
|
+
// otherwise have silently dropped its coverage.
|
|
2784
|
+
"manifest"
|
|
2437
2785
|
]);
|
|
2786
|
+
var HTML_CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set(["srcdoc"]);
|
|
2787
|
+
function isHtmlContentAttribute(name) {
|
|
2788
|
+
return HTML_CONTENT_ATTRIBUTES.has(name.toLowerCase());
|
|
2789
|
+
}
|
|
2790
|
+
function isPolicyAttribute(name) {
|
|
2791
|
+
const lower = name.toLowerCase();
|
|
2792
|
+
return lower === "style" || URL_ATTRIBUTES.has(lower);
|
|
2793
|
+
}
|
|
2438
2794
|
function sanitizeAttributeString(attr, value) {
|
|
2439
2795
|
const lower = attr.toLowerCase();
|
|
2440
2796
|
if (lower === "srcset") return sanitizeSrcset(value);
|
|
2441
2797
|
if (URL_ATTRIBUTES.has(lower)) return sanitizeUrl(value);
|
|
2798
|
+
if (lower === "style") return sanitizeStyleAttribute(value);
|
|
2442
2799
|
return value;
|
|
2443
2800
|
}
|
|
2444
2801
|
|
|
@@ -2771,13 +3128,13 @@ function idle(timeout = 6e4) {
|
|
|
2771
3128
|
|
|
2772
3129
|
// src/browser/permissions.ts
|
|
2773
3130
|
function permissions(name) {
|
|
2774
|
-
const [
|
|
3131
|
+
const [state2, setState] = signal("prompt");
|
|
2775
3132
|
let permissionStatus = null;
|
|
2776
3133
|
let onChange = null;
|
|
2777
3134
|
let disposed = false;
|
|
2778
3135
|
if (typeof navigator === "undefined" || !navigator.permissions) {
|
|
2779
3136
|
setState("unsupported");
|
|
2780
|
-
return { state, dispose: () => {
|
|
3137
|
+
return { state: state2, dispose: () => {
|
|
2781
3138
|
} };
|
|
2782
3139
|
}
|
|
2783
3140
|
navigator.permissions.query({ name }).then((status) => {
|
|
@@ -2789,6 +3146,7 @@ function permissions(name) {
|
|
|
2789
3146
|
};
|
|
2790
3147
|
status.addEventListener("change", onChange);
|
|
2791
3148
|
}).catch(() => {
|
|
3149
|
+
if (disposed) return;
|
|
2792
3150
|
setState("unsupported");
|
|
2793
3151
|
});
|
|
2794
3152
|
function dispose2() {
|
|
@@ -2799,7 +3157,7 @@ function permissions(name) {
|
|
|
2799
3157
|
onChange = null;
|
|
2800
3158
|
}
|
|
2801
3159
|
}
|
|
2802
|
-
return { state, dispose: dispose2 };
|
|
3160
|
+
return { state: state2, dispose: dispose2 };
|
|
2803
3161
|
}
|
|
2804
3162
|
|
|
2805
3163
|
// src/browser/clipboard.ts
|
|
@@ -2807,20 +3165,25 @@ function clipboard() {
|
|
|
2807
3165
|
const [text2, setText] = signal("");
|
|
2808
3166
|
const [copied, setCopied] = signal(false);
|
|
2809
3167
|
let copiedTimer = null;
|
|
3168
|
+
let disposed = false;
|
|
2810
3169
|
async function copy(value) {
|
|
3170
|
+
if (disposed) return;
|
|
2811
3171
|
if (typeof navigator === "undefined" || !navigator.clipboard) {
|
|
2812
3172
|
return;
|
|
2813
3173
|
}
|
|
2814
3174
|
await navigator.clipboard.writeText(value);
|
|
3175
|
+
if (disposed) return;
|
|
2815
3176
|
setText(value);
|
|
2816
3177
|
setCopied(true);
|
|
2817
3178
|
if (copiedTimer !== null) clearTimeout(copiedTimer);
|
|
2818
3179
|
copiedTimer = setTimeout(() => {
|
|
3180
|
+
if (disposed) return;
|
|
2819
3181
|
setCopied(false);
|
|
2820
3182
|
copiedTimer = null;
|
|
2821
3183
|
}, 2e3);
|
|
2822
3184
|
}
|
|
2823
3185
|
function dispose2() {
|
|
3186
|
+
disposed = true;
|
|
2824
3187
|
if (copiedTimer !== null) {
|
|
2825
3188
|
clearTimeout(copiedTimer);
|
|
2826
3189
|
copiedTimer = null;
|
|
@@ -2953,26 +3316,141 @@ function dropZone(element, options) {
|
|
|
2953
3316
|
return { isOver, dispose: dispose2 };
|
|
2954
3317
|
}
|
|
2955
3318
|
|
|
3319
|
+
// src/utils/singletonResource.ts
|
|
3320
|
+
function singletonResource(io) {
|
|
3321
|
+
let original = null;
|
|
3322
|
+
let captured = false;
|
|
3323
|
+
const stack = [];
|
|
3324
|
+
function apply() {
|
|
3325
|
+
if (stack.length > 0) {
|
|
3326
|
+
io.write(stack[stack.length - 1].value);
|
|
3327
|
+
return;
|
|
3328
|
+
}
|
|
3329
|
+
if (captured) {
|
|
3330
|
+
io.write(original);
|
|
3331
|
+
captured = false;
|
|
3332
|
+
original = null;
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
return {
|
|
3336
|
+
acquire(value) {
|
|
3337
|
+
if (!captured) {
|
|
3338
|
+
original = io.read();
|
|
3339
|
+
captured = true;
|
|
3340
|
+
}
|
|
3341
|
+
const entry = { value, released: false };
|
|
3342
|
+
stack.push(entry);
|
|
3343
|
+
apply();
|
|
3344
|
+
return {
|
|
3345
|
+
set(next) {
|
|
3346
|
+
if (entry.released) return;
|
|
3347
|
+
entry.value = next;
|
|
3348
|
+
apply();
|
|
3349
|
+
},
|
|
3350
|
+
release() {
|
|
3351
|
+
if (entry.released) return;
|
|
3352
|
+
entry.released = true;
|
|
3353
|
+
const index = stack.indexOf(entry);
|
|
3354
|
+
if (index !== -1) stack.splice(index, 1);
|
|
3355
|
+
apply();
|
|
3356
|
+
}
|
|
3357
|
+
};
|
|
3358
|
+
},
|
|
3359
|
+
ownerCount() {
|
|
3360
|
+
return stack.length;
|
|
3361
|
+
}
|
|
3362
|
+
};
|
|
3363
|
+
}
|
|
3364
|
+
|
|
3365
|
+
// src/utils/documentResources.ts
|
|
3366
|
+
var INERT_LEASE = {
|
|
3367
|
+
set() {
|
|
3368
|
+
},
|
|
3369
|
+
release() {
|
|
3370
|
+
}
|
|
3371
|
+
};
|
|
3372
|
+
function inertLease() {
|
|
3373
|
+
return INERT_LEASE;
|
|
3374
|
+
}
|
|
3375
|
+
var titleResource = globalSingleton(
|
|
3376
|
+
/* @__PURE__ */ Symbol.for("sibujs.documentTitle.v1"),
|
|
3377
|
+
() => singletonResource({
|
|
3378
|
+
read: () => document.title,
|
|
3379
|
+
write: (value) => {
|
|
3380
|
+
document.title = value;
|
|
3381
|
+
}
|
|
3382
|
+
})
|
|
3383
|
+
);
|
|
3384
|
+
var baseResource = globalSingleton(
|
|
3385
|
+
/* @__PURE__ */ Symbol.for("sibujs.documentBase.v1"),
|
|
3386
|
+
() => singletonResource({
|
|
3387
|
+
read: () => {
|
|
3388
|
+
const el = document.head.querySelector("base");
|
|
3389
|
+
if (!el) return null;
|
|
3390
|
+
return {
|
|
3391
|
+
href: el.getAttribute("href") ?? void 0,
|
|
3392
|
+
target: el.getAttribute("target") ?? void 0
|
|
3393
|
+
};
|
|
3394
|
+
},
|
|
3395
|
+
write: (spec) => {
|
|
3396
|
+
const existing = document.head.querySelector("base");
|
|
3397
|
+
if (spec === null) {
|
|
3398
|
+
existing?.remove();
|
|
3399
|
+
return;
|
|
3400
|
+
}
|
|
3401
|
+
const el = existing ?? document.head.appendChild(document.createElement("base"));
|
|
3402
|
+
if (spec.href === void 0) el.removeAttribute("href");
|
|
3403
|
+
else el.setAttribute("href", spec.href);
|
|
3404
|
+
if (spec.target === void 0) el.removeAttribute("target");
|
|
3405
|
+
else el.setAttribute("target", spec.target);
|
|
3406
|
+
}
|
|
3407
|
+
})
|
|
3408
|
+
);
|
|
3409
|
+
var scrollRestorationResource = globalSingleton(
|
|
3410
|
+
/* @__PURE__ */ Symbol.for("sibujs.historyScrollRestoration.v1"),
|
|
3411
|
+
() => singletonResource({
|
|
3412
|
+
read: () => history.scrollRestoration,
|
|
3413
|
+
write: (mode) => {
|
|
3414
|
+
history.scrollRestoration = mode;
|
|
3415
|
+
}
|
|
3416
|
+
})
|
|
3417
|
+
);
|
|
3418
|
+
function acquireScrollRestorationMode(mode) {
|
|
3419
|
+
if (typeof history === "undefined" || !("scrollRestoration" in history)) {
|
|
3420
|
+
return inertLease();
|
|
3421
|
+
}
|
|
3422
|
+
return scrollRestorationResource.acquire(mode);
|
|
3423
|
+
}
|
|
3424
|
+
function acquireTitle(value) {
|
|
3425
|
+
if (typeof document === "undefined") return inertLease();
|
|
3426
|
+
return titleResource.acquire(value);
|
|
3427
|
+
}
|
|
3428
|
+
function acquireBase(spec) {
|
|
3429
|
+
if (typeof document === "undefined") return inertLease();
|
|
3430
|
+
return baseResource.acquire(spec);
|
|
3431
|
+
}
|
|
3432
|
+
|
|
2956
3433
|
// src/browser/title.ts
|
|
2957
3434
|
function title(value) {
|
|
2958
3435
|
if (typeof document === "undefined") {
|
|
2959
3436
|
return () => {
|
|
2960
3437
|
};
|
|
2961
3438
|
}
|
|
2962
|
-
const previousTitle = document.title;
|
|
2963
3439
|
if (typeof value === "function") {
|
|
3440
|
+
let lease2 = null;
|
|
2964
3441
|
const cleanup3 = effect(() => {
|
|
2965
|
-
|
|
3442
|
+
const next = value();
|
|
3443
|
+
if (lease2) lease2.set(next);
|
|
3444
|
+
else lease2 = acquireTitle(next);
|
|
2966
3445
|
});
|
|
2967
3446
|
return () => {
|
|
2968
3447
|
cleanup3();
|
|
2969
|
-
|
|
3448
|
+
lease2?.release();
|
|
3449
|
+
lease2 = null;
|
|
2970
3450
|
};
|
|
2971
3451
|
}
|
|
2972
|
-
|
|
2973
|
-
return () =>
|
|
2974
|
-
document.title = previousTitle;
|
|
2975
|
-
};
|
|
3452
|
+
const lease = acquireTitle(value);
|
|
3453
|
+
return () => lease.release();
|
|
2976
3454
|
}
|
|
2977
3455
|
|
|
2978
3456
|
// src/browser/colorScheme.ts
|
|
@@ -3286,51 +3764,112 @@ function wakeLock() {
|
|
|
3286
3764
|
};
|
|
3287
3765
|
}
|
|
3288
3766
|
const api = navigator.wakeLock;
|
|
3289
|
-
let
|
|
3767
|
+
let current = null;
|
|
3768
|
+
let desire = false;
|
|
3769
|
+
let generation = 0;
|
|
3770
|
+
let inflight = null;
|
|
3290
3771
|
let disposed = false;
|
|
3291
|
-
|
|
3772
|
+
function publishActive(value) {
|
|
3292
3773
|
if (disposed) return;
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
});
|
|
3305
|
-
}
|
|
3306
|
-
|
|
3774
|
+
setActive(value);
|
|
3775
|
+
}
|
|
3776
|
+
function syncReleasedState() {
|
|
3777
|
+
if (current?.released) {
|
|
3778
|
+
current = null;
|
|
3779
|
+
publishActive(false);
|
|
3780
|
+
}
|
|
3781
|
+
}
|
|
3782
|
+
function discard(sentinel) {
|
|
3783
|
+
if (sentinel.released) return;
|
|
3784
|
+
sentinel.release().catch((err) => {
|
|
3785
|
+
reportError(err, { phase: "async", name: "wakeLock(discard)" });
|
|
3786
|
+
});
|
|
3787
|
+
}
|
|
3788
|
+
async function request() {
|
|
3789
|
+
if (disposed) return;
|
|
3790
|
+
desire = true;
|
|
3791
|
+
syncReleasedState();
|
|
3792
|
+
if (current && !current.released) {
|
|
3793
|
+
publishActive(true);
|
|
3794
|
+
return;
|
|
3307
3795
|
}
|
|
3796
|
+
if (inflight) return inflight;
|
|
3797
|
+
const myGeneration = ++generation;
|
|
3798
|
+
const attempt = (async () => {
|
|
3799
|
+
try {
|
|
3800
|
+
const sentinel = await api.request("screen");
|
|
3801
|
+
if (disposed || !desire || generation !== myGeneration) {
|
|
3802
|
+
discard(sentinel);
|
|
3803
|
+
return;
|
|
3804
|
+
}
|
|
3805
|
+
if (sentinel.released) {
|
|
3806
|
+
publishActive(false);
|
|
3807
|
+
return;
|
|
3808
|
+
}
|
|
3809
|
+
try {
|
|
3810
|
+
sentinel.addEventListener("release", () => {
|
|
3811
|
+
if (current !== sentinel) return;
|
|
3812
|
+
current = null;
|
|
3813
|
+
publishActive(false);
|
|
3814
|
+
});
|
|
3815
|
+
} catch (listenerError) {
|
|
3816
|
+
discard(sentinel);
|
|
3817
|
+
publishActive(false);
|
|
3818
|
+
reportError(listenerError, { phase: "async", name: "wakeLock(listener registration)" });
|
|
3819
|
+
return;
|
|
3820
|
+
}
|
|
3821
|
+
if (sentinel.released) {
|
|
3822
|
+
publishActive(false);
|
|
3823
|
+
return;
|
|
3824
|
+
}
|
|
3825
|
+
current = sentinel;
|
|
3826
|
+
publishActive(true);
|
|
3827
|
+
} catch (err) {
|
|
3828
|
+
if (generation === myGeneration) publishActive(false);
|
|
3829
|
+
reportError(err, { phase: "async", name: "wakeLock(request)" });
|
|
3830
|
+
}
|
|
3831
|
+
})();
|
|
3832
|
+
inflight = attempt;
|
|
3833
|
+
void attempt.finally(() => {
|
|
3834
|
+
if (inflight === attempt) inflight = null;
|
|
3835
|
+
}).catch(() => {
|
|
3836
|
+
});
|
|
3837
|
+
return attempt;
|
|
3308
3838
|
}
|
|
3309
3839
|
async function release() {
|
|
3310
|
-
|
|
3311
|
-
|
|
3840
|
+
desire = false;
|
|
3841
|
+
generation++;
|
|
3842
|
+
inflight = null;
|
|
3843
|
+
const held = current;
|
|
3844
|
+
current = null;
|
|
3845
|
+
publishActive(false);
|
|
3846
|
+
if (held && !held.released) {
|
|
3847
|
+
await held.release();
|
|
3312
3848
|
}
|
|
3313
|
-
sentinel = null;
|
|
3314
|
-
setActive(false);
|
|
3315
3849
|
}
|
|
3316
3850
|
const onVisibility = () => {
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
});
|
|
3323
|
-
}
|
|
3851
|
+
syncReleasedState();
|
|
3852
|
+
if (!desire || current || document.hidden) return;
|
|
3853
|
+
request().catch((err) => {
|
|
3854
|
+
reportError(err, { phase: "async", name: "wakeLock(visibility re-acquire)" });
|
|
3855
|
+
});
|
|
3324
3856
|
};
|
|
3325
3857
|
document.addEventListener("visibilitychange", onVisibility);
|
|
3326
3858
|
function dispose2() {
|
|
3327
|
-
disposed
|
|
3859
|
+
if (disposed) return;
|
|
3860
|
+
desire = false;
|
|
3861
|
+
generation++;
|
|
3862
|
+
inflight = null;
|
|
3328
3863
|
document.removeEventListener("visibilitychange", onVisibility);
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3864
|
+
const held = current;
|
|
3865
|
+
current = null;
|
|
3866
|
+
setActive(false);
|
|
3867
|
+
disposed = true;
|
|
3868
|
+
if (held && !held.released) {
|
|
3869
|
+
held.release().catch((err) => {
|
|
3870
|
+
reportError(err, { phase: "async", name: "wakeLock(dispose release)" });
|
|
3871
|
+
});
|
|
3872
|
+
}
|
|
3334
3873
|
}
|
|
3335
3874
|
return { active, request, release, dispose: dispose2 };
|
|
3336
3875
|
}
|
|
@@ -3845,14 +4384,14 @@ function imageLoader(src) {
|
|
|
3845
4384
|
|
|
3846
4385
|
// src/patterns/machine.ts
|
|
3847
4386
|
function machine(config) {
|
|
3848
|
-
const [
|
|
4387
|
+
const [state2, setState] = signal(config.initial);
|
|
3849
4388
|
const [context2, setContext] = signal(config.context || {});
|
|
3850
4389
|
const initialDef = config.states[config.initial];
|
|
3851
4390
|
if (initialDef?.entry) {
|
|
3852
4391
|
initialDef.entry(context2());
|
|
3853
4392
|
}
|
|
3854
4393
|
function send(event) {
|
|
3855
|
-
const currentState =
|
|
4394
|
+
const currentState = state2();
|
|
3856
4395
|
const stateDef = config.states[currentState];
|
|
3857
4396
|
if (!stateDef?.on) return;
|
|
3858
4397
|
const transition2 = stateDef.on[event];
|
|
@@ -3884,10 +4423,10 @@ function machine(config) {
|
|
|
3884
4423
|
}
|
|
3885
4424
|
}
|
|
3886
4425
|
function matches(s2) {
|
|
3887
|
-
return
|
|
4426
|
+
return state2() === s2;
|
|
3888
4427
|
}
|
|
3889
4428
|
function can(event) {
|
|
3890
|
-
const currentState =
|
|
4429
|
+
const currentState = state2();
|
|
3891
4430
|
const stateDef = config.states[currentState];
|
|
3892
4431
|
if (!stateDef?.on) return false;
|
|
3893
4432
|
const transition2 = stateDef.on[event];
|
|
@@ -3898,7 +4437,7 @@ function machine(config) {
|
|
|
3898
4437
|
}
|
|
3899
4438
|
return true;
|
|
3900
4439
|
}
|
|
3901
|
-
return { state, context: context2, send, matches, can };
|
|
4440
|
+
return { state: state2, context: context2, send, matches, can };
|
|
3902
4441
|
}
|
|
3903
4442
|
|
|
3904
4443
|
// src/patterns/persist.ts
|
|
@@ -4007,116 +4546,258 @@ function optimistic(initialValue) {
|
|
|
4007
4546
|
}
|
|
4008
4547
|
} finally {
|
|
4009
4548
|
inflightCount--;
|
|
4010
|
-
if (inflightCount
|
|
4549
|
+
if (inflightCount <= 0) {
|
|
4550
|
+
inflightCount = 0;
|
|
4551
|
+
setPending(false);
|
|
4552
|
+
}
|
|
4011
4553
|
}
|
|
4012
4554
|
}
|
|
4013
4555
|
return { value, pending, update };
|
|
4014
4556
|
}
|
|
4015
4557
|
function optimisticList(initialValue) {
|
|
4016
|
-
|
|
4558
|
+
let nextRowId = 0;
|
|
4559
|
+
let nextKey = 0;
|
|
4560
|
+
let nextOpId = 0;
|
|
4561
|
+
const records = /* @__PURE__ */ new Map();
|
|
4562
|
+
let order = [];
|
|
4563
|
+
for (const value of initialValue) {
|
|
4564
|
+
const row = { id: nextRowId++, key: nextKey++, base: value, claims: [], visible: true, refs: 0 };
|
|
4565
|
+
records.set(row.id, row);
|
|
4566
|
+
order.push(row.id);
|
|
4567
|
+
}
|
|
4568
|
+
function effectiveValue(row) {
|
|
4569
|
+
const claims = row.claims;
|
|
4570
|
+
return claims.length > 0 ? claims[claims.length - 1].value : row.base;
|
|
4571
|
+
}
|
|
4572
|
+
function valueBelow(row, opId) {
|
|
4573
|
+
const claims = row.claims;
|
|
4574
|
+
for (let i2 = claims.length - 1; i2 >= 0; i2--) {
|
|
4575
|
+
if (claims[i2].opId < opId) return claims[i2].value;
|
|
4576
|
+
}
|
|
4577
|
+
return row.base;
|
|
4578
|
+
}
|
|
4579
|
+
function stake(row, opId, value) {
|
|
4580
|
+
const claim = { opId, value, settled: false };
|
|
4581
|
+
const claims = row.claims;
|
|
4582
|
+
let at = claims.length;
|
|
4583
|
+
while (at > 0 && claims[at - 1].opId > opId) at--;
|
|
4584
|
+
claims.splice(at, 0, claim);
|
|
4585
|
+
return claim;
|
|
4586
|
+
}
|
|
4587
|
+
function dropClaim(row, claim) {
|
|
4588
|
+
const at = row.claims.indexOf(claim);
|
|
4589
|
+
if (at !== -1) row.claims.splice(at, 1);
|
|
4590
|
+
}
|
|
4591
|
+
function collapse(row) {
|
|
4592
|
+
const claims = row.claims;
|
|
4593
|
+
let i2 = 0;
|
|
4594
|
+
while (i2 < claims.length && claims[i2].settled) {
|
|
4595
|
+
row.base = claims[i2].value;
|
|
4596
|
+
i2++;
|
|
4597
|
+
}
|
|
4598
|
+
if (i2 > 0) claims.splice(0, i2);
|
|
4599
|
+
}
|
|
4600
|
+
const [items, setItems] = signal(order.map((id) => effectiveValue(records.get(id))));
|
|
4017
4601
|
const [pending, setPending] = signal(false);
|
|
4018
|
-
let
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
const id = ++tempIdCounter;
|
|
4025
|
-
if (item !== null && typeof item === "object") {
|
|
4026
|
-
itemIds.set(item, id);
|
|
4027
|
-
}
|
|
4028
|
-
idToItem.set(id, item);
|
|
4029
|
-
return id;
|
|
4602
|
+
let inflight = 0;
|
|
4603
|
+
const retiredRowIds = /* @__PURE__ */ new Set();
|
|
4604
|
+
function project() {
|
|
4605
|
+
const out = new Array(order.length);
|
|
4606
|
+
for (let i2 = 0; i2 < order.length; i2++) out[i2] = effectiveValue(records.get(order[i2]));
|
|
4607
|
+
return out;
|
|
4030
4608
|
}
|
|
4031
|
-
function
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4609
|
+
function commit(mutate) {
|
|
4610
|
+
batch(() => {
|
|
4611
|
+
let visibleChanged = false;
|
|
4612
|
+
try {
|
|
4613
|
+
visibleChanged = mutate();
|
|
4614
|
+
} finally {
|
|
4615
|
+
setPending(inflight > 0);
|
|
4616
|
+
if (visibleChanged) setItems(project());
|
|
4036
4617
|
}
|
|
4037
|
-
|
|
4618
|
+
});
|
|
4619
|
+
}
|
|
4620
|
+
function hide(id) {
|
|
4621
|
+
const row = records.get(id);
|
|
4622
|
+
if (!row || !row.visible) return false;
|
|
4623
|
+
row.visible = false;
|
|
4624
|
+
order = order.filter((x) => x !== id);
|
|
4625
|
+
return true;
|
|
4626
|
+
}
|
|
4627
|
+
function restore(ids) {
|
|
4628
|
+
const restorable = [];
|
|
4629
|
+
for (const id of ids) {
|
|
4630
|
+
const row = records.get(id);
|
|
4631
|
+
if (!row || row.visible || retiredRowIds.has(id)) continue;
|
|
4632
|
+
restorable.push(id);
|
|
4633
|
+
}
|
|
4634
|
+
if (restorable.length === 0) return false;
|
|
4635
|
+
const merged = new Array(order.length + restorable.length);
|
|
4636
|
+
let i2 = 0;
|
|
4637
|
+
let j = 0;
|
|
4638
|
+
let w = 0;
|
|
4639
|
+
while (i2 < order.length && j < restorable.length) {
|
|
4640
|
+
const a2 = records.get(order[i2]).key;
|
|
4641
|
+
const b2 = records.get(restorable[j]).key;
|
|
4642
|
+
merged[w++] = a2 <= b2 ? order[i2++] : restorable[j++];
|
|
4643
|
+
}
|
|
4644
|
+
while (i2 < order.length) merged[w++] = order[i2++];
|
|
4645
|
+
while (j < restorable.length) merged[w++] = restorable[j++];
|
|
4646
|
+
for (const id of restorable) records.get(id).visible = true;
|
|
4647
|
+
order = merged;
|
|
4648
|
+
return true;
|
|
4649
|
+
}
|
|
4650
|
+
function retain(ids) {
|
|
4651
|
+
for (const id of ids) {
|
|
4652
|
+
const row = records.get(id);
|
|
4653
|
+
if (row) row.refs++;
|
|
4038
4654
|
}
|
|
4039
|
-
return -1;
|
|
4040
4655
|
}
|
|
4041
|
-
function
|
|
4042
|
-
const
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4656
|
+
function release(ids) {
|
|
4657
|
+
for (const id of ids) {
|
|
4658
|
+
const row = records.get(id);
|
|
4659
|
+
if (!row) continue;
|
|
4660
|
+
row.refs--;
|
|
4661
|
+
if (row.refs <= 0 && !row.visible) {
|
|
4662
|
+
records.delete(id);
|
|
4663
|
+
retiredRowIds.delete(id);
|
|
4664
|
+
}
|
|
4665
|
+
}
|
|
4046
4666
|
}
|
|
4047
|
-
function
|
|
4048
|
-
|
|
4049
|
-
|
|
4667
|
+
function settle(heldIds) {
|
|
4668
|
+
release(heldIds);
|
|
4669
|
+
inflight--;
|
|
4670
|
+
if (inflight <= 0) {
|
|
4671
|
+
inflight = 0;
|
|
4672
|
+
retiredRowIds.clear();
|
|
4673
|
+
if (records.size !== order.length) {
|
|
4674
|
+
for (const [id, row] of records) {
|
|
4675
|
+
if (!row.visible) records.delete(id);
|
|
4676
|
+
}
|
|
4677
|
+
}
|
|
4050
4678
|
}
|
|
4051
|
-
inflightCount--;
|
|
4052
|
-
if (inflightCount === 0) setPending(false);
|
|
4053
4679
|
}
|
|
4054
4680
|
async function add(item, asyncAction) {
|
|
4055
|
-
const
|
|
4056
|
-
const
|
|
4057
|
-
|
|
4058
|
-
|
|
4681
|
+
const opId = ++nextOpId;
|
|
4682
|
+
const row = { id: nextRowId++, key: nextKey++, base: item, claims: [], visible: false, refs: 1 };
|
|
4683
|
+
const claim = stake(row, opId, item);
|
|
4684
|
+
commit(() => {
|
|
4685
|
+
records.set(row.id, row);
|
|
4686
|
+
row.visible = true;
|
|
4687
|
+
order = [...order, row.id];
|
|
4688
|
+
inflight++;
|
|
4689
|
+
return true;
|
|
4690
|
+
});
|
|
4691
|
+
let result;
|
|
4692
|
+
let failed = false;
|
|
4059
4693
|
try {
|
|
4060
|
-
|
|
4061
|
-
setItems((current) => {
|
|
4062
|
-
const idx = findIndexById(current, id);
|
|
4063
|
-
if (idx >= 0) {
|
|
4064
|
-
const next = [...current];
|
|
4065
|
-
next[idx] = result;
|
|
4066
|
-
return next;
|
|
4067
|
-
}
|
|
4068
|
-
return [...current, result];
|
|
4069
|
-
});
|
|
4070
|
-
idToItem.delete(id);
|
|
4071
|
-
end(myVersion);
|
|
4694
|
+
result = await asyncAction();
|
|
4072
4695
|
} catch {
|
|
4073
|
-
|
|
4074
|
-
end(myVersion, () => setItems(prev));
|
|
4696
|
+
failed = true;
|
|
4075
4697
|
}
|
|
4698
|
+
commit(() => {
|
|
4699
|
+
try {
|
|
4700
|
+
if (failed) {
|
|
4701
|
+
retiredRowIds.add(row.id);
|
|
4702
|
+
dropClaim(row, claim);
|
|
4703
|
+
return hide(row.id);
|
|
4704
|
+
}
|
|
4705
|
+
const before = effectiveValue(row);
|
|
4706
|
+
claim.value = result;
|
|
4707
|
+
claim.settled = true;
|
|
4708
|
+
collapse(row);
|
|
4709
|
+
return row.visible && !Object.is(before, effectiveValue(row));
|
|
4710
|
+
} finally {
|
|
4711
|
+
settle([row.id]);
|
|
4712
|
+
}
|
|
4713
|
+
});
|
|
4076
4714
|
}
|
|
4077
4715
|
async function remove(predicate, asyncAction) {
|
|
4078
|
-
const
|
|
4079
|
-
|
|
4080
|
-
const
|
|
4716
|
+
const opId = ++nextOpId;
|
|
4717
|
+
const matchedIds = [];
|
|
4718
|
+
for (const id of order) {
|
|
4719
|
+
if (predicate(valueBelow(records.get(id), opId))) matchedIds.push(id);
|
|
4720
|
+
}
|
|
4721
|
+
commit(() => {
|
|
4722
|
+
retain(matchedIds);
|
|
4723
|
+
inflight++;
|
|
4724
|
+
if (matchedIds.length === 0) return false;
|
|
4725
|
+
const removing = new Set(matchedIds);
|
|
4726
|
+
for (const id of matchedIds) {
|
|
4727
|
+
const row = records.get(id);
|
|
4728
|
+
if (row) row.visible = false;
|
|
4729
|
+
}
|
|
4730
|
+
order = order.filter((id) => !removing.has(id));
|
|
4731
|
+
return true;
|
|
4732
|
+
});
|
|
4733
|
+
let failed = false;
|
|
4081
4734
|
try {
|
|
4082
4735
|
await asyncAction();
|
|
4083
|
-
end(myVersion);
|
|
4084
4736
|
} catch {
|
|
4085
|
-
|
|
4737
|
+
failed = true;
|
|
4086
4738
|
}
|
|
4739
|
+
commit(() => {
|
|
4740
|
+
try {
|
|
4741
|
+
if (!failed) {
|
|
4742
|
+
for (const id of matchedIds) retiredRowIds.add(id);
|
|
4743
|
+
return false;
|
|
4744
|
+
}
|
|
4745
|
+
return restore(matchedIds);
|
|
4746
|
+
} finally {
|
|
4747
|
+
settle(matchedIds);
|
|
4748
|
+
}
|
|
4749
|
+
});
|
|
4087
4750
|
}
|
|
4088
4751
|
async function updateItem(predicate, patch, asyncAction) {
|
|
4089
|
-
const
|
|
4090
|
-
const
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4752
|
+
const opId = ++nextOpId;
|
|
4753
|
+
const prepared = [];
|
|
4754
|
+
for (const id of order) {
|
|
4755
|
+
const row = records.get(id);
|
|
4756
|
+
const seen = valueBelow(row, opId);
|
|
4757
|
+
if (!predicate(seen)) continue;
|
|
4758
|
+
prepared.push({ row, nextValue: { ...seen, ...patch } });
|
|
4759
|
+
}
|
|
4760
|
+
const preparedIds = prepared.map((p2) => p2.row.id);
|
|
4761
|
+
const claims = [];
|
|
4762
|
+
commit(() => {
|
|
4763
|
+
retain(preparedIds);
|
|
4764
|
+
inflight++;
|
|
4765
|
+
let visibleChanged = false;
|
|
4766
|
+
for (const p2 of prepared) {
|
|
4767
|
+
const before = effectiveValue(p2.row);
|
|
4768
|
+
claims.push(stake(p2.row, opId, p2.nextValue));
|
|
4769
|
+
if (p2.row.visible && !Object.is(before, effectiveValue(p2.row))) visibleChanged = true;
|
|
4770
|
+
}
|
|
4771
|
+
return visibleChanged;
|
|
4772
|
+
});
|
|
4773
|
+
let result;
|
|
4774
|
+
let failed = false;
|
|
4103
4775
|
try {
|
|
4104
|
-
|
|
4105
|
-
setItems(
|
|
4106
|
-
(current) => current.map((item) => {
|
|
4107
|
-
if (item !== null && typeof item === "object") {
|
|
4108
|
-
const existingId = itemIds.get(item);
|
|
4109
|
-
if (existingId !== void 0 && patchedIds.includes(existingId)) return result;
|
|
4110
|
-
}
|
|
4111
|
-
return item;
|
|
4112
|
-
})
|
|
4113
|
-
);
|
|
4114
|
-
for (const id of patchedIds) idToItem.delete(id);
|
|
4115
|
-
end(myVersion);
|
|
4776
|
+
result = await asyncAction();
|
|
4116
4777
|
} catch {
|
|
4117
|
-
|
|
4118
|
-
end(myVersion, () => setItems(prev));
|
|
4778
|
+
failed = true;
|
|
4119
4779
|
}
|
|
4780
|
+
commit(() => {
|
|
4781
|
+
try {
|
|
4782
|
+
let visibleChanged = false;
|
|
4783
|
+
for (let i2 = 0; i2 < claims.length; i2++) {
|
|
4784
|
+
const row = prepared[i2].row;
|
|
4785
|
+
const claim = claims[i2];
|
|
4786
|
+
const before = effectiveValue(row);
|
|
4787
|
+
if (failed) {
|
|
4788
|
+
dropClaim(row, claim);
|
|
4789
|
+
} else {
|
|
4790
|
+
claim.value = result;
|
|
4791
|
+
claim.settled = true;
|
|
4792
|
+
}
|
|
4793
|
+
collapse(row);
|
|
4794
|
+
if (row.visible && !Object.is(before, effectiveValue(row))) visibleChanged = true;
|
|
4795
|
+
}
|
|
4796
|
+
return visibleChanged;
|
|
4797
|
+
} finally {
|
|
4798
|
+
settle(preparedIds);
|
|
4799
|
+
}
|
|
4800
|
+
});
|
|
4120
4801
|
}
|
|
4121
4802
|
return {
|
|
4122
4803
|
items,
|
|
@@ -4129,16 +4810,16 @@ function optimisticList(initialValue) {
|
|
|
4129
4810
|
|
|
4130
4811
|
// src/patterns/timeTravel.ts
|
|
4131
4812
|
function timeline(initial, maxHistory = 100) {
|
|
4132
|
-
const [
|
|
4813
|
+
const [history2, setHistory] = signal([initial]);
|
|
4133
4814
|
const [index, setIndex] = signal(0);
|
|
4134
|
-
const value = derived(() =>
|
|
4815
|
+
const value = derived(() => history2()[index()]);
|
|
4135
4816
|
const canUndo = derived(() => index() > 0);
|
|
4136
|
-
const canRedo = derived(() => index() <
|
|
4817
|
+
const canRedo = derived(() => index() < history2().length - 1);
|
|
4137
4818
|
function set(next) {
|
|
4138
4819
|
const current = value();
|
|
4139
4820
|
const newValue = typeof next === "function" ? next(current) : next;
|
|
4140
4821
|
if (Object.is(newValue, current)) return;
|
|
4141
|
-
const hist =
|
|
4822
|
+
const hist = history2();
|
|
4142
4823
|
const idx = index();
|
|
4143
4824
|
const newHistory = hist.slice(0, idx + 1);
|
|
4144
4825
|
newHistory.push(newValue);
|
|
@@ -4170,12 +4851,12 @@ function timeline(initial, maxHistory = 100) {
|
|
|
4170
4851
|
});
|
|
4171
4852
|
}
|
|
4172
4853
|
function jumpTo(targetIndex) {
|
|
4173
|
-
const hist =
|
|
4854
|
+
const hist = history2();
|
|
4174
4855
|
if (targetIndex >= 0 && targetIndex < hist.length) {
|
|
4175
4856
|
setIndex(targetIndex);
|
|
4176
4857
|
}
|
|
4177
4858
|
}
|
|
4178
|
-
return { value, set, undo, redo, canUndo, canRedo, history, index, reset, jumpTo };
|
|
4859
|
+
return { value, set, undo, redo, canUndo, canRedo, history: history2, index, reset, jumpTo };
|
|
4179
4860
|
}
|
|
4180
4861
|
|
|
4181
4862
|
// src/patterns/globalStore.ts
|
|
@@ -4422,6 +5103,14 @@ function createGuard(validator) {
|
|
|
4422
5103
|
|
|
4423
5104
|
// src/core/rendering/dispose.ts
|
|
4424
5105
|
var elementDisposers = /* @__PURE__ */ new WeakMap();
|
|
5106
|
+
var MAX_DRAIN_TEARDOWNS = 1e4;
|
|
5107
|
+
function reportDrainRunaway(label2, executed, remaining) {
|
|
5108
|
+
if (typeof console === "undefined") return;
|
|
5109
|
+
console.error(
|
|
5110
|
+
`[SibuJS ${label2}] runaway cleanup: stopped after running ${executed} teardowns with ${remaining} still queued. Cleanup production did not stabilize before the teardown safety ceiling was reached \u2014 the remaining work was NOT run.`,
|
|
5111
|
+
{ executed, remaining }
|
|
5112
|
+
);
|
|
5113
|
+
}
|
|
4425
5114
|
var _isDev5 = isDev();
|
|
4426
5115
|
var activeBindingCount = 0;
|
|
4427
5116
|
function registerDisposer(node, teardown) {
|
|
@@ -4446,40 +5135,47 @@ function dispose(node) {
|
|
|
4446
5135
|
}
|
|
4447
5136
|
for (let i2 = order.length - 1; i2 >= 0; i2--) {
|
|
4448
5137
|
const current = order[i2];
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
d();
|
|
4457
|
-
} catch (err) {
|
|
4458
|
-
if (_isDev5 && typeof console !== "undefined") {
|
|
4459
|
-
console.warn("[SibuJS] Disposer threw during cleanup:", err);
|
|
4460
|
-
}
|
|
4461
|
-
}
|
|
4462
|
-
}
|
|
4463
|
-
let extraPasses = 0;
|
|
4464
|
-
while (extraPasses++ < 8) {
|
|
4465
|
-
const added = elementDisposers.get(current);
|
|
4466
|
-
if (!added || added.length === 0) break;
|
|
4467
|
-
const moreSnapshot = added.slice();
|
|
5138
|
+
if (elementDisposers.has(current)) {
|
|
5139
|
+
let executed = 0;
|
|
5140
|
+
let runaway = false;
|
|
5141
|
+
while (!runaway) {
|
|
5142
|
+
const pending = elementDisposers.get(current);
|
|
5143
|
+
if (!pending || pending.length === 0) break;
|
|
5144
|
+
const snapshot = pending.slice();
|
|
4468
5145
|
elementDisposers.delete(current);
|
|
4469
|
-
if (_isDev5) activeBindingCount -=
|
|
4470
|
-
for (
|
|
5146
|
+
if (_isDev5) activeBindingCount -= snapshot.length;
|
|
5147
|
+
for (let i3 = 0; i3 < snapshot.length; i3++) {
|
|
5148
|
+
if (executed >= MAX_DRAIN_TEARDOWNS) {
|
|
5149
|
+
const rest = snapshot.slice(i3);
|
|
5150
|
+
const added = elementDisposers.get(current);
|
|
5151
|
+
elementDisposers.set(current, added ? rest.concat(added) : rest);
|
|
5152
|
+
if (_isDev5) activeBindingCount += rest.length;
|
|
5153
|
+
reportDrainRunaway("dispose", executed, rest.length + (added?.length ?? 0));
|
|
5154
|
+
runaway = true;
|
|
5155
|
+
break;
|
|
5156
|
+
}
|
|
5157
|
+
executed++;
|
|
4471
5158
|
try {
|
|
4472
|
-
|
|
5159
|
+
snapshot[i3]();
|
|
4473
5160
|
} catch (err) {
|
|
4474
|
-
|
|
4475
|
-
console.warn("[SibuJS] Disposer threw during cleanup:", err);
|
|
4476
|
-
}
|
|
5161
|
+
reportError(err, { phase: "cleanup", name: "disposer" });
|
|
4477
5162
|
}
|
|
4478
5163
|
}
|
|
4479
5164
|
}
|
|
4480
5165
|
}
|
|
4481
5166
|
}
|
|
4482
5167
|
}
|
|
5168
|
+
function replaceChildrenSafely(parent, ...next) {
|
|
5169
|
+
for (let i2 = 0; i2 < next.length; i2++) {
|
|
5170
|
+
const node = next[i2];
|
|
5171
|
+
node.parentNode?.removeChild(node);
|
|
5172
|
+
}
|
|
5173
|
+
const current = Array.from(parent.childNodes);
|
|
5174
|
+
for (let i2 = 0; i2 < current.length; i2++) {
|
|
5175
|
+
dispose(current[i2]);
|
|
5176
|
+
}
|
|
5177
|
+
parent.replaceChildren(...next);
|
|
5178
|
+
}
|
|
4483
5179
|
|
|
4484
5180
|
// src/ui/transition.ts
|
|
4485
5181
|
function transition(element, options = {}) {
|
|
@@ -4811,7 +5507,9 @@ function TransitionGroup(options) {
|
|
|
4811
5507
|
// src/ui/viewTransition.ts
|
|
4812
5508
|
function viewTransition(callback) {
|
|
4813
5509
|
const [isTransitioning, setIsTransitioning] = signal(false);
|
|
5510
|
+
let inflight = 0;
|
|
4814
5511
|
async function start() {
|
|
5512
|
+
inflight++;
|
|
4815
5513
|
setIsTransitioning(true);
|
|
4816
5514
|
try {
|
|
4817
5515
|
if (typeof document !== "undefined" && "startViewTransition" in document && typeof document.startViewTransition === "function") {
|
|
@@ -4821,7 +5519,11 @@ function viewTransition(callback) {
|
|
|
4821
5519
|
await callback();
|
|
4822
5520
|
}
|
|
4823
5521
|
} finally {
|
|
4824
|
-
|
|
5522
|
+
inflight--;
|
|
5523
|
+
if (inflight <= 0) {
|
|
5524
|
+
inflight = 0;
|
|
5525
|
+
setIsTransitioning(false);
|
|
5526
|
+
}
|
|
4825
5527
|
}
|
|
4826
5528
|
}
|
|
4827
5529
|
return { start, isTransitioning };
|
|
@@ -4982,6 +5684,8 @@ function bindField(field, extras) {
|
|
|
4982
5684
|
}
|
|
4983
5685
|
};
|
|
4984
5686
|
return {
|
|
5687
|
+
// The field's own getter, no longer widened to `unknown` — that widening is
|
|
5688
|
+
// what forced every consumer to cast. (TYPE-010)
|
|
4985
5689
|
value: field.value,
|
|
4986
5690
|
on: mergedOn,
|
|
4987
5691
|
onElement,
|
|
@@ -5534,7 +6238,7 @@ function announce(message, priority = "polite") {
|
|
|
5534
6238
|
|
|
5535
6239
|
// src/ui/scopedStyle.ts
|
|
5536
6240
|
var _scope = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.scopedStyle.v1"), () => ({ n: 0 }));
|
|
5537
|
-
function
|
|
6241
|
+
function decodeCssEscapes2(css) {
|
|
5538
6242
|
return css.replace(/\\([0-9a-f]{1,6})[ \t\n\r\f]?|\\([^\n])/gi, (_match, hex, ch) => {
|
|
5539
6243
|
if (hex) {
|
|
5540
6244
|
const code2 = Number.parseInt(hex, 16);
|
|
@@ -5551,7 +6255,7 @@ function decodeCssEscapes(css) {
|
|
|
5551
6255
|
});
|
|
5552
6256
|
}
|
|
5553
6257
|
function sanitizeCSS(css) {
|
|
5554
|
-
let sanitized =
|
|
6258
|
+
let sanitized = decodeCssEscapes2(css);
|
|
5555
6259
|
sanitized = sanitized.replace(/@import\s+[^;]+;/gi, "/* @import removed */");
|
|
5556
6260
|
sanitized = sanitized.replace(/url\s*\(\s*(?:"[^"]*"|'[^']*'|[^)]*)\s*\)/gi, "/* url() removed */");
|
|
5557
6261
|
sanitized = sanitized.replace(/expression\s*\(\s*(?:"[^"]*"|'[^']*'|[^)]*)\s*\)/gi, "/* expression() removed */");
|
|
@@ -5559,21 +6263,169 @@ function sanitizeCSS(css) {
|
|
|
5559
6263
|
sanitized = sanitized.replace(/behavior\s*:[^;]+;/gi, "/* behavior removed */");
|
|
5560
6264
|
return sanitized;
|
|
5561
6265
|
}
|
|
6266
|
+
function firstCompoundEnd(selector) {
|
|
6267
|
+
let parens = 0;
|
|
6268
|
+
let brackets = 0;
|
|
6269
|
+
let quote = null;
|
|
6270
|
+
for (let i2 = 0; i2 < selector.length; i2++) {
|
|
6271
|
+
const ch = selector[i2];
|
|
6272
|
+
if (quote) {
|
|
6273
|
+
if (ch === "\\") i2++;
|
|
6274
|
+
else if (ch === quote) quote = null;
|
|
6275
|
+
continue;
|
|
6276
|
+
}
|
|
6277
|
+
if (ch === '"' || ch === "'") {
|
|
6278
|
+
quote = ch;
|
|
6279
|
+
continue;
|
|
6280
|
+
}
|
|
6281
|
+
if (ch === "\\") {
|
|
6282
|
+
i2++;
|
|
6283
|
+
continue;
|
|
6284
|
+
}
|
|
6285
|
+
if (ch === "(") parens++;
|
|
6286
|
+
else if (ch === ")") parens--;
|
|
6287
|
+
else if (ch === "[") brackets++;
|
|
6288
|
+
else if (ch === "]") brackets--;
|
|
6289
|
+
if (parens > 0 || brackets > 0) continue;
|
|
6290
|
+
if (ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f") return i2;
|
|
6291
|
+
if (ch === ">" || ch === "+" || ch === "~") return i2;
|
|
6292
|
+
if (ch === ":" && selector[i2 + 1] === ":") return i2;
|
|
6293
|
+
}
|
|
6294
|
+
return selector.length;
|
|
6295
|
+
}
|
|
6296
|
+
function scopeSelector(selector, attr) {
|
|
6297
|
+
const end = firstCompoundEnd(selector);
|
|
6298
|
+
const selfForm = `${selector.slice(0, end)}[${attr}]${selector.slice(end)}`;
|
|
6299
|
+
const descendantForm = `[${attr}] ${selector}`;
|
|
6300
|
+
return `${descendantForm}, ${selfForm}`;
|
|
6301
|
+
}
|
|
6302
|
+
function skipNonCode(text2, i2) {
|
|
6303
|
+
const ch = text2[i2];
|
|
6304
|
+
if (ch === "\\") return i2 + 2;
|
|
6305
|
+
if (ch === '"' || ch === "'") {
|
|
6306
|
+
let j = i2 + 1;
|
|
6307
|
+
while (j < text2.length) {
|
|
6308
|
+
if (text2[j] === "\\") {
|
|
6309
|
+
j += 2;
|
|
6310
|
+
continue;
|
|
6311
|
+
}
|
|
6312
|
+
if (text2[j] === ch) return j + 1;
|
|
6313
|
+
j++;
|
|
6314
|
+
}
|
|
6315
|
+
return text2.length;
|
|
6316
|
+
}
|
|
6317
|
+
if (ch === "/" && text2[i2 + 1] === "*") {
|
|
6318
|
+
const end = text2.indexOf("*/", i2 + 2);
|
|
6319
|
+
return end === -1 ? text2.length : end + 2;
|
|
6320
|
+
}
|
|
6321
|
+
return -1;
|
|
6322
|
+
}
|
|
6323
|
+
function splitSelectorList(prelude) {
|
|
6324
|
+
const parts = [];
|
|
6325
|
+
let parens = 0;
|
|
6326
|
+
let brackets = 0;
|
|
6327
|
+
let start = 0;
|
|
6328
|
+
for (let i2 = 0; i2 < prelude.length; i2++) {
|
|
6329
|
+
const skip = skipNonCode(prelude, i2);
|
|
6330
|
+
if (skip !== -1) {
|
|
6331
|
+
i2 = skip - 1;
|
|
6332
|
+
continue;
|
|
6333
|
+
}
|
|
6334
|
+
const ch = prelude[i2];
|
|
6335
|
+
if (ch === "(") parens++;
|
|
6336
|
+
else if (ch === ")") parens--;
|
|
6337
|
+
else if (ch === "[") brackets++;
|
|
6338
|
+
else if (ch === "]") brackets--;
|
|
6339
|
+
else if (ch === "," && parens === 0 && brackets === 0) {
|
|
6340
|
+
parts.push(prelude.slice(start, i2));
|
|
6341
|
+
start = i2 + 1;
|
|
6342
|
+
}
|
|
6343
|
+
}
|
|
6344
|
+
parts.push(prelude.slice(start));
|
|
6345
|
+
return parts;
|
|
6346
|
+
}
|
|
6347
|
+
function findBlockEnd(css, openBrace) {
|
|
6348
|
+
let depth = 0;
|
|
6349
|
+
for (let i2 = openBrace; i2 < css.length; i2++) {
|
|
6350
|
+
const skip = skipNonCode(css, i2);
|
|
6351
|
+
if (skip !== -1) {
|
|
6352
|
+
i2 = skip - 1;
|
|
6353
|
+
continue;
|
|
6354
|
+
}
|
|
6355
|
+
const ch = css[i2];
|
|
6356
|
+
if (ch === "{") depth++;
|
|
6357
|
+
else if (ch === "}") {
|
|
6358
|
+
depth--;
|
|
6359
|
+
if (depth === 0) return i2;
|
|
6360
|
+
}
|
|
6361
|
+
}
|
|
6362
|
+
return css.length;
|
|
6363
|
+
}
|
|
6364
|
+
var NESTED_AT_RULE = /^@(media|supports|layer|container|scope|document)\b/i;
|
|
6365
|
+
function scopeStylesheet(css, attr) {
|
|
6366
|
+
let out = "";
|
|
6367
|
+
let preludeStart = 0;
|
|
6368
|
+
let parens = 0;
|
|
6369
|
+
let brackets = 0;
|
|
6370
|
+
let i2 = 0;
|
|
6371
|
+
while (i2 < css.length) {
|
|
6372
|
+
const skip = skipNonCode(css, i2);
|
|
6373
|
+
if (skip !== -1) {
|
|
6374
|
+
i2 = skip;
|
|
6375
|
+
continue;
|
|
6376
|
+
}
|
|
6377
|
+
const ch = css[i2];
|
|
6378
|
+
if (ch === "(") {
|
|
6379
|
+
parens++;
|
|
6380
|
+
i2++;
|
|
6381
|
+
continue;
|
|
6382
|
+
}
|
|
6383
|
+
if (ch === ")") {
|
|
6384
|
+
parens--;
|
|
6385
|
+
i2++;
|
|
6386
|
+
continue;
|
|
6387
|
+
}
|
|
6388
|
+
if (ch === "[") {
|
|
6389
|
+
brackets++;
|
|
6390
|
+
i2++;
|
|
6391
|
+
continue;
|
|
6392
|
+
}
|
|
6393
|
+
if (ch === "]") {
|
|
6394
|
+
brackets--;
|
|
6395
|
+
i2++;
|
|
6396
|
+
continue;
|
|
6397
|
+
}
|
|
6398
|
+
if (ch === "{" && parens === 0 && brackets === 0) {
|
|
6399
|
+
const prelude = css.slice(preludeStart, i2);
|
|
6400
|
+
const bodyEnd = findBlockEnd(css, i2);
|
|
6401
|
+
const body2 = css.slice(i2 + 1, bodyEnd);
|
|
6402
|
+
const trimmed = prelude.trim();
|
|
6403
|
+
const lead = prelude.slice(0, prelude.length - prelude.trimStart().length);
|
|
6404
|
+
if (trimmed === "") {
|
|
6405
|
+
out += `${prelude}{${body2}}`;
|
|
6406
|
+
} else if (trimmed.startsWith("@")) {
|
|
6407
|
+
out += NESTED_AT_RULE.test(trimmed) ? `${prelude}{${scopeStylesheet(body2, attr)}}` : `${prelude}{${body2}}`;
|
|
6408
|
+
} else {
|
|
6409
|
+
const scoped = splitSelectorList(trimmed).map((part) => {
|
|
6410
|
+
const selector = part.trim();
|
|
6411
|
+
return selector ? scopeSelector(selector, attr) : part;
|
|
6412
|
+
}).join(", ");
|
|
6413
|
+
out += `${lead}${scoped}{${body2}}`;
|
|
6414
|
+
}
|
|
6415
|
+
i2 = bodyEnd + 1;
|
|
6416
|
+
preludeStart = i2;
|
|
6417
|
+
continue;
|
|
6418
|
+
}
|
|
6419
|
+
i2++;
|
|
6420
|
+
}
|
|
6421
|
+
out += css.slice(preludeStart);
|
|
6422
|
+
return out;
|
|
6423
|
+
}
|
|
5562
6424
|
function scopedStyle(css) {
|
|
5563
6425
|
const id = `sibu-s${_scope.n++}`;
|
|
5564
6426
|
const attr = `data-${id}`;
|
|
5565
6427
|
const safeCss = sanitizeCSS(css);
|
|
5566
|
-
const scopedCSS = safeCss
|
|
5567
|
-
const trimmed = selector.trim();
|
|
5568
|
-
if (trimmed.startsWith("@") || trimmed.startsWith("from") || trimmed.startsWith("to") || /^\d+%$/.test(trimmed)) {
|
|
5569
|
-
return match;
|
|
5570
|
-
}
|
|
5571
|
-
const pseudoIdx = trimmed.indexOf("::");
|
|
5572
|
-
if (pseudoIdx >= 0) {
|
|
5573
|
-
return `${trimmed.slice(0, pseudoIdx)}[${attr}]${trimmed.slice(pseudoIdx)}${delimiter}`;
|
|
5574
|
-
}
|
|
5575
|
-
return `${trimmed}[${attr}]${delimiter}`;
|
|
5576
|
-
});
|
|
6428
|
+
const scopedCSS = scopeStylesheet(safeCss, attr);
|
|
5577
6429
|
if (typeof document !== "undefined") {
|
|
5578
6430
|
const styleEl = document.createElement("style");
|
|
5579
6431
|
styleEl.setAttribute("data-sibu-scope", id);
|
|
@@ -5589,34 +6441,95 @@ function withScopedStyle(css, component) {
|
|
|
5589
6441
|
style2 = scopedStyle(css);
|
|
5590
6442
|
}
|
|
5591
6443
|
const el = component(props);
|
|
5592
|
-
|
|
6444
|
+
el.setAttribute(style2.attr, "");
|
|
5593
6445
|
return el;
|
|
5594
6446
|
};
|
|
5595
6447
|
}
|
|
5596
|
-
function applyScopeRecursive(element, attr) {
|
|
5597
|
-
element.setAttribute(attr, "");
|
|
5598
|
-
for (const child of Array.from(element.children)) {
|
|
5599
|
-
if (child instanceof HTMLElement) {
|
|
5600
|
-
applyScopeRecursive(child, attr);
|
|
5601
|
-
}
|
|
5602
|
-
}
|
|
5603
|
-
}
|
|
5604
6448
|
function removeScopedStyle(scopeId) {
|
|
5605
6449
|
const el = document.head.querySelector(`style[data-sibu-scope="${scopeId}"]`);
|
|
5606
6450
|
if (el) el.remove();
|
|
5607
6451
|
}
|
|
5608
6452
|
|
|
5609
|
-
// src/
|
|
6453
|
+
// src/utils/setSafeAttribute.ts
|
|
5610
6454
|
var _isDev6 = isDev();
|
|
6455
|
+
var HTML_NS = "http://www.w3.org/1999/xhtml";
|
|
6456
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
6457
|
+
var XLINK_NS = "http://www.w3.org/1999/xlink";
|
|
6458
|
+
var XML_NS = "http://www.w3.org/XML/1998/namespace";
|
|
6459
|
+
var BOOLEAN_IDL_ATTRS = /* @__PURE__ */ new Set(["checked", "disabled", "selected"]);
|
|
6460
|
+
function isHtmlElement(el) {
|
|
6461
|
+
return el.namespaceURI === null || el.namespaceURI === HTML_NS;
|
|
6462
|
+
}
|
|
5611
6463
|
function setProp(el, key, val) {
|
|
5612
6464
|
el[key] = val;
|
|
5613
6465
|
}
|
|
5614
|
-
function
|
|
5615
|
-
if (
|
|
5616
|
-
|
|
6466
|
+
function namespaceFor(el, name) {
|
|
6467
|
+
if (el.namespaceURI !== SVG_NS) return null;
|
|
6468
|
+
const lower = name.toLowerCase();
|
|
6469
|
+
if (lower.startsWith("xlink:")) return XLINK_NS;
|
|
6470
|
+
if (lower.startsWith("xml:")) return XML_NS;
|
|
6471
|
+
return null;
|
|
6472
|
+
}
|
|
6473
|
+
function setSafeAttribute(el, name, value, options = {}) {
|
|
6474
|
+
const ns = namespaceFor(el, name);
|
|
6475
|
+
const localName = ns ? name.slice(name.indexOf(":") + 1) : name;
|
|
6476
|
+
if (isEventHandlerAttr(name)) {
|
|
6477
|
+
if (_isDev6) {
|
|
6478
|
+
devWarn(
|
|
6479
|
+
`${options.label ?? "setSafeAttribute"}: refusing to set event-handler attribute "${name}". Its value would be evaluated as JavaScript. Use on:{ ${name.slice(2)}: fn } instead.`
|
|
6480
|
+
);
|
|
6481
|
+
}
|
|
6482
|
+
if (ns) el.removeAttributeNS(ns, localName);
|
|
6483
|
+
else el.removeAttribute(name);
|
|
6484
|
+
return false;
|
|
6485
|
+
}
|
|
6486
|
+
if (value == null) {
|
|
6487
|
+
if (ns) el.removeAttributeNS(ns, localName);
|
|
6488
|
+
else el.removeAttribute(name);
|
|
6489
|
+
return true;
|
|
6490
|
+
}
|
|
6491
|
+
if (isHtmlContentAttribute(name)) {
|
|
6492
|
+
if (_isDev6) {
|
|
5617
6493
|
devWarn(
|
|
5618
|
-
|
|
6494
|
+
`${options.label ?? "setSafeAttribute"}: refusing to set "${name}". The browser parses this attribute as a nested HTML document, so a generic string value cannot be made safe \u2014 attribute escaping is undone before the parse. Build the frame's content another way.`
|
|
5619
6495
|
);
|
|
6496
|
+
}
|
|
6497
|
+
if (ns) el.removeAttributeNS(ns, localName);
|
|
6498
|
+
else el.removeAttribute(name);
|
|
6499
|
+
return false;
|
|
6500
|
+
}
|
|
6501
|
+
const html2 = isHtmlElement(el);
|
|
6502
|
+
const idlName = html2 ? name.toLowerCase() : name;
|
|
6503
|
+
if (typeof value === "boolean") {
|
|
6504
|
+
if (html2 && BOOLEAN_IDL_ATTRS.has(idlName) && idlName in el) {
|
|
6505
|
+
setProp(el, idlName, value);
|
|
6506
|
+
} else if (value) {
|
|
6507
|
+
if (ns) el.setAttributeNS(ns, name, "");
|
|
6508
|
+
else el.setAttribute(name, "");
|
|
6509
|
+
} else if (ns) {
|
|
6510
|
+
el.removeAttributeNS(ns, localName);
|
|
6511
|
+
} else {
|
|
6512
|
+
el.removeAttribute(name);
|
|
6513
|
+
}
|
|
6514
|
+
return true;
|
|
6515
|
+
}
|
|
6516
|
+
const str = String(value);
|
|
6517
|
+
if (options.syncValueProperty !== false && html2 && (idlName === "value" || idlName === "checked") && idlName in el) {
|
|
6518
|
+
setProp(el, idlName, idlName === "checked" ? Boolean(value) : str);
|
|
6519
|
+
return true;
|
|
6520
|
+
}
|
|
6521
|
+
const safe = sanitizeAttributeString(name, str);
|
|
6522
|
+
const current = ns ? el.getAttributeNS(ns, localName) : el.getAttribute(name);
|
|
6523
|
+
if (current === safe) return true;
|
|
6524
|
+
if (ns) el.setAttributeNS(ns, name, safe);
|
|
6525
|
+
else el.setAttribute(name, safe);
|
|
6526
|
+
return true;
|
|
6527
|
+
}
|
|
6528
|
+
|
|
6529
|
+
// src/reactivity/bindAttribute.ts
|
|
6530
|
+
function bindAttribute(el, attr, getter) {
|
|
6531
|
+
if (isEventHandlerAttr(attr)) {
|
|
6532
|
+
setSafeAttribute(el, attr, "", { label: `bindAttribute("${attr}")` });
|
|
5620
6533
|
return () => {
|
|
5621
6534
|
};
|
|
5622
6535
|
}
|
|
@@ -5625,28 +6538,12 @@ function bindAttribute(el, attr, getter) {
|
|
|
5625
6538
|
try {
|
|
5626
6539
|
value = getter();
|
|
5627
6540
|
} catch (err) {
|
|
5628
|
-
|
|
5629
|
-
devWarn(`bindAttribute: getter for "${attr}" threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
6541
|
+
reportError(err, { phase: "binding", name: `bindAttribute("${attr}")`, node: el });
|
|
5630
6542
|
return;
|
|
5631
6543
|
}
|
|
5632
|
-
|
|
5633
|
-
if (attr in el && (attr === "checked" || attr === "disabled" || attr === "selected")) {
|
|
5634
|
-
setProp(el, attr, value);
|
|
5635
|
-
} else if (value) {
|
|
5636
|
-
el.setAttribute(attr, "");
|
|
5637
|
-
} else {
|
|
5638
|
-
el.removeAttribute(attr);
|
|
5639
|
-
}
|
|
5640
|
-
return;
|
|
5641
|
-
}
|
|
5642
|
-
const str = String(value);
|
|
5643
|
-
if ((attr === "value" || attr === "checked") && attr in el) {
|
|
5644
|
-
setProp(el, attr, attr === "checked" ? Boolean(value) : str);
|
|
5645
|
-
} else {
|
|
5646
|
-
el.setAttribute(attr, sanitizeAttributeString(attr, str));
|
|
5647
|
-
}
|
|
6544
|
+
setSafeAttribute(el, attr, value, { label: `bindAttribute("${attr}")` });
|
|
5648
6545
|
}
|
|
5649
|
-
return reactiveBinding2(commit);
|
|
6546
|
+
return reactiveBinding2(commit, el);
|
|
5650
6547
|
}
|
|
5651
6548
|
|
|
5652
6549
|
// src/ui/reactiveAttr.ts
|
|
@@ -5656,14 +6553,8 @@ function bindAttrs(el, attrs) {
|
|
|
5656
6553
|
if (typeof value === "function") {
|
|
5657
6554
|
const teardown = bindAttribute(el, attr, value);
|
|
5658
6555
|
teardowns.push(teardown);
|
|
5659
|
-
} else if (typeof value === "boolean") {
|
|
5660
|
-
if (value) {
|
|
5661
|
-
el.setAttribute(attr, "");
|
|
5662
|
-
} else {
|
|
5663
|
-
el.removeAttribute(attr);
|
|
5664
|
-
}
|
|
5665
6556
|
} else {
|
|
5666
|
-
el
|
|
6557
|
+
setSafeAttribute(el, attr, value, { label: "bindAttrs" });
|
|
5667
6558
|
}
|
|
5668
6559
|
}
|
|
5669
6560
|
return () => {
|
|
@@ -5674,11 +6565,7 @@ function bindAttrs(el, attrs) {
|
|
|
5674
6565
|
}
|
|
5675
6566
|
function bindBoolAttr(el, attr, getter) {
|
|
5676
6567
|
if (typeof getter !== "function") {
|
|
5677
|
-
|
|
5678
|
-
el.setAttribute(attr, "");
|
|
5679
|
-
} else {
|
|
5680
|
-
el.removeAttribute(attr);
|
|
5681
|
-
}
|
|
6568
|
+
setSafeAttribute(el, attr, getter, { label: "bindBoolAttr" });
|
|
5682
6569
|
return () => {
|
|
5683
6570
|
};
|
|
5684
6571
|
}
|
|
@@ -5690,11 +6577,7 @@ function bindBoolAttr(el, attr, getter) {
|
|
|
5690
6577
|
} catch {
|
|
5691
6578
|
return;
|
|
5692
6579
|
}
|
|
5693
|
-
|
|
5694
|
-
el.setAttribute(attr, "");
|
|
5695
|
-
} else {
|
|
5696
|
-
el.removeAttribute(attr);
|
|
5697
|
-
}
|
|
6580
|
+
setSafeAttribute(el, attr, Boolean(value), { label: "bindBoolAttr" });
|
|
5698
6581
|
}
|
|
5699
6582
|
const teardown = track2(commit);
|
|
5700
6583
|
return teardown;
|
|
@@ -5702,7 +6585,7 @@ function bindBoolAttr(el, attr, getter) {
|
|
|
5702
6585
|
function bindData(el, key, getter) {
|
|
5703
6586
|
const dataAttr = `data-${key}`;
|
|
5704
6587
|
if (typeof getter !== "function") {
|
|
5705
|
-
el
|
|
6588
|
+
setSafeAttribute(el, dataAttr, getter, { label: "bindData" });
|
|
5706
6589
|
return () => {
|
|
5707
6590
|
};
|
|
5708
6591
|
}
|
|
@@ -5842,31 +6725,58 @@ function infiniteScroll(options) {
|
|
|
5842
6725
|
const sentinelRef = { current: null };
|
|
5843
6726
|
let observer = null;
|
|
5844
6727
|
let disposed = false;
|
|
6728
|
+
let generation = 0;
|
|
6729
|
+
function safeHasMore() {
|
|
6730
|
+
try {
|
|
6731
|
+
return hasMore();
|
|
6732
|
+
} catch (err) {
|
|
6733
|
+
reportError(err, { phase: "async", name: "infiniteScroll(hasMore)" });
|
|
6734
|
+
return false;
|
|
6735
|
+
}
|
|
6736
|
+
}
|
|
6737
|
+
function ownsAttachment(owner) {
|
|
6738
|
+
return !disposed && generation === owner.generation && observer === owner.observer && _current === owner.sentinel;
|
|
6739
|
+
}
|
|
5845
6740
|
function createObserver() {
|
|
5846
6741
|
if (typeof IntersectionObserver === "undefined") return;
|
|
5847
|
-
|
|
6742
|
+
let createdObserver;
|
|
6743
|
+
createdObserver = new IntersectionObserver(
|
|
5848
6744
|
(entries) => {
|
|
5849
6745
|
const entry = entries[0];
|
|
5850
|
-
if (entry?.isIntersecting
|
|
5851
|
-
|
|
5852
|
-
|
|
6746
|
+
if (!entry?.isIntersecting) return;
|
|
6747
|
+
const sentinel = _current;
|
|
6748
|
+
if (!sentinel) return;
|
|
6749
|
+
const owner = { generation, observer: createdObserver, sentinel };
|
|
6750
|
+
if (!ownsAttachment(owner) || loading()) return;
|
|
6751
|
+
const shouldLoad = safeHasMore();
|
|
6752
|
+
if (!shouldLoad || !ownsAttachment(owner)) return;
|
|
6753
|
+
void loadMore(owner);
|
|
5853
6754
|
},
|
|
5854
6755
|
{ threshold }
|
|
5855
6756
|
);
|
|
6757
|
+
observer = createdObserver;
|
|
5856
6758
|
if (sentinelRef.current) {
|
|
5857
|
-
|
|
6759
|
+
createdObserver.observe(sentinelRef.current);
|
|
5858
6760
|
}
|
|
5859
6761
|
}
|
|
5860
|
-
async function loadMore() {
|
|
5861
|
-
setLoading(true);
|
|
6762
|
+
async function loadMore(owner) {
|
|
5862
6763
|
try {
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
6764
|
+
setLoading(true);
|
|
6765
|
+
if (!ownsAttachment(owner)) return;
|
|
6766
|
+
try {
|
|
6767
|
+
await onLoadMore();
|
|
6768
|
+
} catch (err) {
|
|
6769
|
+
reportError(err, { phase: "async", name: "infiniteScroll(onLoadMore)" });
|
|
5869
6770
|
}
|
|
6771
|
+
if (!ownsAttachment(owner)) return;
|
|
6772
|
+
setLoading(false);
|
|
6773
|
+
if (!ownsAttachment(owner)) return;
|
|
6774
|
+
const shouldContinue = safeHasMore();
|
|
6775
|
+
if (!shouldContinue || !ownsAttachment(owner)) return;
|
|
6776
|
+
owner.observer.unobserve(owner.sentinel);
|
|
6777
|
+
owner.observer.observe(owner.sentinel);
|
|
6778
|
+
} catch (err) {
|
|
6779
|
+
reportError(err, { phase: "async", name: "infiniteScroll(loadMore)" });
|
|
5870
6780
|
}
|
|
5871
6781
|
}
|
|
5872
6782
|
const originalRef = sentinelRef;
|
|
@@ -5876,7 +6786,10 @@ function infiniteScroll(options) {
|
|
|
5876
6786
|
return _current;
|
|
5877
6787
|
},
|
|
5878
6788
|
set(el) {
|
|
6789
|
+
if (el === _current) return;
|
|
5879
6790
|
_current = el;
|
|
6791
|
+
generation++;
|
|
6792
|
+
setLoading(false);
|
|
5880
6793
|
if (observer) {
|
|
5881
6794
|
observer.disconnect();
|
|
5882
6795
|
observer = null;
|
|
@@ -5889,10 +6802,12 @@ function infiniteScroll(options) {
|
|
|
5889
6802
|
});
|
|
5890
6803
|
function dispose2() {
|
|
5891
6804
|
disposed = true;
|
|
6805
|
+
generation++;
|
|
5892
6806
|
if (observer) {
|
|
5893
6807
|
observer.disconnect();
|
|
5894
6808
|
observer = null;
|
|
5895
6809
|
}
|
|
6810
|
+
setLoading(false);
|
|
5896
6811
|
}
|
|
5897
6812
|
return { sentinelRef: originalRef, loading, dispose: dispose2 };
|
|
5898
6813
|
}
|
|
@@ -6214,7 +7129,7 @@ var DOMPool = class {
|
|
|
6214
7129
|
this.pools.set(tag, pool);
|
|
6215
7130
|
}
|
|
6216
7131
|
if (pool.length >= this.maxSize) return;
|
|
6217
|
-
element
|
|
7132
|
+
replaceChildrenSafely(element);
|
|
6218
7133
|
element.className = "";
|
|
6219
7134
|
element.removeAttribute("style");
|
|
6220
7135
|
element.removeAttribute("id");
|
|
@@ -6313,29 +7228,178 @@ function noSideEffect(fn) {
|
|
|
6313
7228
|
return fn;
|
|
6314
7229
|
}
|
|
6315
7230
|
|
|
7231
|
+
// src/utils/metaRefresh.ts
|
|
7232
|
+
function isAsciiWhitespace(ch) {
|
|
7233
|
+
return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
|
|
7234
|
+
}
|
|
7235
|
+
function trimAsciiWhitespace(value) {
|
|
7236
|
+
let start = 0;
|
|
7237
|
+
let end = value.length;
|
|
7238
|
+
while (start < end && isAsciiWhitespace(value[start])) start++;
|
|
7239
|
+
while (end > start && isAsciiWhitespace(value[end - 1])) end--;
|
|
7240
|
+
return value.slice(start, end);
|
|
7241
|
+
}
|
|
7242
|
+
var forbidden = (reason) => ({ kind: "forbidden", reason });
|
|
7243
|
+
function parseMetaRefreshContent(content) {
|
|
7244
|
+
const raw = trimAsciiWhitespace(stripControlChars(content));
|
|
7245
|
+
if (raw === "") return forbidden("empty content");
|
|
7246
|
+
let i2 = 0;
|
|
7247
|
+
while (i2 < raw.length && raw[i2] >= "0" && raw[i2] <= "9") i2++;
|
|
7248
|
+
if (i2 === 0) return forbidden("missing or non-numeric delay");
|
|
7249
|
+
const delay = Number.parseInt(raw.slice(0, i2), 10);
|
|
7250
|
+
if (!Number.isFinite(delay)) return forbidden("unparseable delay");
|
|
7251
|
+
while (i2 < raw.length && isAsciiWhitespace(raw[i2])) i2++;
|
|
7252
|
+
if (i2 === raw.length) return { kind: "delay-only", delay };
|
|
7253
|
+
if (raw[i2] !== ";" && raw[i2] !== ",") return forbidden("unexpected text after delay");
|
|
7254
|
+
i2++;
|
|
7255
|
+
while (i2 < raw.length && isAsciiWhitespace(raw[i2])) i2++;
|
|
7256
|
+
const keyStart = i2;
|
|
7257
|
+
while (i2 < raw.length && !isAsciiWhitespace(raw[i2]) && raw[i2] !== "=") i2++;
|
|
7258
|
+
const key = raw.slice(keyStart, i2).toLowerCase();
|
|
7259
|
+
if (key !== "url") return forbidden(`unsupported refresh key ${JSON.stringify(key)}`);
|
|
7260
|
+
while (i2 < raw.length && isAsciiWhitespace(raw[i2])) i2++;
|
|
7261
|
+
if (raw[i2] !== "=") return forbidden("missing '=' after url");
|
|
7262
|
+
i2++;
|
|
7263
|
+
while (i2 < raw.length && isAsciiWhitespace(raw[i2])) i2++;
|
|
7264
|
+
let destination;
|
|
7265
|
+
const quote = raw[i2];
|
|
7266
|
+
if (quote === '"' || quote === "'") {
|
|
7267
|
+
const close = raw.indexOf(quote, i2 + 1);
|
|
7268
|
+
if (close === -1) return forbidden("unterminated quoted destination");
|
|
7269
|
+
destination = raw.slice(i2 + 1, close);
|
|
7270
|
+
const rest = trimAsciiWhitespace(raw.slice(close + 1));
|
|
7271
|
+
if (rest !== "") return forbidden("trailing text after quoted destination");
|
|
7272
|
+
} else {
|
|
7273
|
+
destination = trimAsciiWhitespace(raw.slice(i2));
|
|
7274
|
+
if (/[;,]/.test(destination)) return forbidden("multiple refresh assignments");
|
|
7275
|
+
}
|
|
7276
|
+
if (destination === "") return forbidden("empty destination");
|
|
7277
|
+
const sanitizedDestination = sanitizeUrl(destination);
|
|
7278
|
+
if (sanitizedDestination === "") return forbidden("destination uses a disallowed protocol");
|
|
7279
|
+
return { kind: "allowed", delay, destination, sanitizedDestination };
|
|
7280
|
+
}
|
|
7281
|
+
function resolveMetaRefreshPolicy(attributes) {
|
|
7282
|
+
let httpEquiv;
|
|
7283
|
+
let content;
|
|
7284
|
+
let duplicateName;
|
|
7285
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7286
|
+
for (const [key, value] of attributes) {
|
|
7287
|
+
const canonical = canonicalAttrName(key);
|
|
7288
|
+
if (seen.has(canonical)) duplicateName = canonical;
|
|
7289
|
+
seen.add(canonical);
|
|
7290
|
+
if (canonical === "http-equiv") httpEquiv = value;
|
|
7291
|
+
else if (canonical === "content") content = value;
|
|
7292
|
+
}
|
|
7293
|
+
if (duplicateName !== void 0) {
|
|
7294
|
+
return forbidden(`duplicate case-insensitive attribute ${JSON.stringify(duplicateName)}`);
|
|
7295
|
+
}
|
|
7296
|
+
if (typeof httpEquiv !== "string") return { kind: "not-refresh" };
|
|
7297
|
+
if (trimAsciiWhitespace(stripControlChars(httpEquiv)).toLowerCase() !== "refresh") {
|
|
7298
|
+
return { kind: "not-refresh" };
|
|
7299
|
+
}
|
|
7300
|
+
if (typeof content !== "string") return { kind: "not-refresh" };
|
|
7301
|
+
return parseMetaRefreshContent(content);
|
|
7302
|
+
}
|
|
7303
|
+
function normalizeMetaAttributes(props) {
|
|
7304
|
+
const out = /* @__PURE__ */ new Map();
|
|
7305
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7306
|
+
for (const key of Object.keys(props)) {
|
|
7307
|
+
const canonical = canonicalAttrName(key);
|
|
7308
|
+
if (seen.has(canonical)) return null;
|
|
7309
|
+
seen.add(canonical);
|
|
7310
|
+
out.set(key, props[key]);
|
|
7311
|
+
}
|
|
7312
|
+
return out;
|
|
7313
|
+
}
|
|
7314
|
+
function isForbiddenMetaEntry(props) {
|
|
7315
|
+
const normalized = normalizeMetaAttributes(props);
|
|
7316
|
+
if (normalized === null) return true;
|
|
7317
|
+
return resolveMetaRefreshPolicy(normalized).kind === "forbidden";
|
|
7318
|
+
}
|
|
7319
|
+
|
|
7320
|
+
// src/utils/headEntry.ts
|
|
7321
|
+
var SAFE_HEAD_ATTR_NAME = /^[A-Za-z_:][-A-Za-z0-9_.:]*$/;
|
|
7322
|
+
function isEmittableHeadAttrName(name) {
|
|
7323
|
+
if (!SAFE_HEAD_ATTR_NAME.test(name)) return false;
|
|
7324
|
+
const canonical = canonicalAttrName(name);
|
|
7325
|
+
if (isEventHandlerAttr(canonical)) return false;
|
|
7326
|
+
if (isHtmlContentAttribute(canonical)) return false;
|
|
7327
|
+
return true;
|
|
7328
|
+
}
|
|
7329
|
+
function sanitizeHeadAttrValue(canonical, value) {
|
|
7330
|
+
if (!isPolicyAttribute(canonical)) return value;
|
|
7331
|
+
const safe = sanitizeAttributeString(canonical, value);
|
|
7332
|
+
return safe === "" ? null : safe;
|
|
7333
|
+
}
|
|
7334
|
+
function findDuplicateAttributeName(names) {
|
|
7335
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7336
|
+
for (const name of names) {
|
|
7337
|
+
const canonical = canonicalAttrName(name);
|
|
7338
|
+
if (seen.has(canonical)) return canonical;
|
|
7339
|
+
seen.add(canonical);
|
|
7340
|
+
}
|
|
7341
|
+
return null;
|
|
7342
|
+
}
|
|
7343
|
+
function planEntry(props, resolver, applyRefreshPolicy) {
|
|
7344
|
+
const rawNames = Object.keys(props);
|
|
7345
|
+
const duplicate = findDuplicateAttributeName(rawNames);
|
|
7346
|
+
if (duplicate !== null) {
|
|
7347
|
+
return { kind: "drop", reason: `duplicate case-insensitive attribute ${JSON.stringify(duplicate)}` };
|
|
7348
|
+
}
|
|
7349
|
+
const attributes = /* @__PURE__ */ new Map();
|
|
7350
|
+
let reactive = false;
|
|
7351
|
+
for (const name of rawNames) {
|
|
7352
|
+
if (!Object.hasOwn(props, name)) continue;
|
|
7353
|
+
if (!isEmittableHeadAttrName(name)) continue;
|
|
7354
|
+
const authored = props[name];
|
|
7355
|
+
if (resolver.isReactiveValue?.(authored)) reactive = true;
|
|
7356
|
+
const canonical = canonicalAttrName(name);
|
|
7357
|
+
const sanitized = sanitizeHeadAttrValue(canonical, resolver.resolveValue(name, authored));
|
|
7358
|
+
if (sanitized === null) continue;
|
|
7359
|
+
attributes.set(canonical, sanitized);
|
|
7360
|
+
}
|
|
7361
|
+
if (attributes.size === 0) return { kind: "drop", reason: "no effective attributes remain" };
|
|
7362
|
+
const refresh = applyRefreshPolicy ? resolveMetaRefreshPolicy(attributes) : { kind: "not-refresh" };
|
|
7363
|
+
if (refresh.kind === "forbidden") return { kind: "drop", reason: refresh.reason };
|
|
7364
|
+
if (reactive && (refresh.kind === "allowed" || refresh.kind === "delay-only")) {
|
|
7365
|
+
return { kind: "drop", reason: "a reactive meta entry may not publish a native refresh directive" };
|
|
7366
|
+
}
|
|
7367
|
+
return { kind: "publish", attributes, refresh };
|
|
7368
|
+
}
|
|
7369
|
+
function planMetaEntry(props, resolver) {
|
|
7370
|
+
return planEntry(props, resolver, true);
|
|
7371
|
+
}
|
|
7372
|
+
function planHeadElementEntry(props, resolver) {
|
|
7373
|
+
return planEntry(props, resolver, false);
|
|
7374
|
+
}
|
|
7375
|
+
var STATIC_VALUE_RESOLVER = {
|
|
7376
|
+
resolveValue: (_name, value) => String(value)
|
|
7377
|
+
};
|
|
7378
|
+
function escapeHeadAttrValue(value) {
|
|
7379
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
7380
|
+
}
|
|
7381
|
+
function serializeHeadEntry(tag, attrs) {
|
|
7382
|
+
const plan = tag === "meta" ? planMetaEntry(attrs, STATIC_VALUE_RESOLVER) : planHeadElementEntry(attrs, STATIC_VALUE_RESOLVER);
|
|
7383
|
+
if (plan.kind === "drop") return "";
|
|
7384
|
+
const pairs = Array.from(plan.attributes, ([name, value]) => `${name}="${escapeHeadAttrValue(value)}"`).join(" ");
|
|
7385
|
+
return `<${tag} ${pairs} />`;
|
|
7386
|
+
}
|
|
7387
|
+
|
|
6316
7388
|
// src/platform/ssr.ts
|
|
6317
|
-
function
|
|
6318
|
-
return
|
|
7389
|
+
function isPolicyAttr(lowerName) {
|
|
7390
|
+
return isPolicyAttribute(lowerName);
|
|
7391
|
+
}
|
|
7392
|
+
function isForbiddenSsrAttr(lowerName) {
|
|
7393
|
+
return isHtmlContentAttribute(lowerName);
|
|
7394
|
+
}
|
|
7395
|
+
function sanitizeSsrAttributeValue(lowerName, value) {
|
|
7396
|
+
return sanitizeAttributeString(lowerName, value);
|
|
6319
7397
|
}
|
|
6320
7398
|
var _isDev8 = isDev();
|
|
6321
7399
|
var SAFE_ATTR_NAME = /^[A-Za-z_:][-A-Za-z0-9_.:]*$/;
|
|
6322
7400
|
function isSafeAttrName(name) {
|
|
6323
7401
|
return SAFE_ATTR_NAME.test(name);
|
|
6324
7402
|
}
|
|
6325
|
-
var URL_ATTRS = /* @__PURE__ */ new Set([
|
|
6326
|
-
"href",
|
|
6327
|
-
"src",
|
|
6328
|
-
"action",
|
|
6329
|
-
"formaction",
|
|
6330
|
-
"cite",
|
|
6331
|
-
"poster",
|
|
6332
|
-
"background",
|
|
6333
|
-
"srcset",
|
|
6334
|
-
"ping",
|
|
6335
|
-
"manifest",
|
|
6336
|
-
"data",
|
|
6337
|
-
"xlink:href"
|
|
6338
|
-
]);
|
|
6339
7403
|
function ssrErrorComment(err) {
|
|
6340
7404
|
if (_isDev8) {
|
|
6341
7405
|
const msg = escapeHtml(err instanceof Error ? err.message : String(err));
|
|
@@ -6393,10 +7457,11 @@ function renderToString(element) {
|
|
|
6393
7457
|
const rawName = attr.name;
|
|
6394
7458
|
if (!isSafeAttrName(rawName)) continue;
|
|
6395
7459
|
if (isEventHandlerAttr(rawName)) continue;
|
|
7460
|
+
if (isForbiddenSsrAttr(rawName)) continue;
|
|
6396
7461
|
const lowerName = rawName.toLowerCase();
|
|
6397
7462
|
let value = attr.value;
|
|
6398
|
-
if (
|
|
6399
|
-
value =
|
|
7463
|
+
if (isPolicyAttr(lowerName)) {
|
|
7464
|
+
value = sanitizeSsrAttributeValue(lowerName, value);
|
|
6400
7465
|
if (!value) continue;
|
|
6401
7466
|
}
|
|
6402
7467
|
html2 += ` ${rawName}="${escapeAttr(value)}"`;
|
|
@@ -6437,9 +7502,20 @@ function hydrate(component, container, options = {}) {
|
|
|
6437
7502
|
}
|
|
6438
7503
|
}
|
|
6439
7504
|
}
|
|
6440
|
-
container
|
|
7505
|
+
replaceChildrenSafely(container, clientTree);
|
|
6441
7506
|
container.setAttribute("data-sibu-hydrated", "true");
|
|
6442
7507
|
}
|
|
7508
|
+
function directTextOf(node) {
|
|
7509
|
+
let out = "";
|
|
7510
|
+
for (const child of Array.from(node.childNodes)) {
|
|
7511
|
+
if (child.nodeType === 3) out += child.nodeValue ?? "";
|
|
7512
|
+
}
|
|
7513
|
+
return out.replace(/\s+/g, " ").trim();
|
|
7514
|
+
}
|
|
7515
|
+
function disposeAndReplace(outgoing, next) {
|
|
7516
|
+
dispose(outgoing);
|
|
7517
|
+
outgoing.replaceWith(next);
|
|
7518
|
+
}
|
|
6443
7519
|
function collectMismatches(serverNode, clientNode, path2, out, max2 = 5) {
|
|
6444
7520
|
if (out.length >= max2) return;
|
|
6445
7521
|
const nodePath = path2 || clientNode?.tagName?.toLowerCase() || "(root)";
|
|
@@ -6515,6 +7591,18 @@ function collectMismatches(serverNode, clientNode, path2, out, max2 = 5) {
|
|
|
6515
7591
|
});
|
|
6516
7592
|
}
|
|
6517
7593
|
}
|
|
7594
|
+
const serverText = directTextOf(serverNode);
|
|
7595
|
+
const clientText = directTextOf(clientNode);
|
|
7596
|
+
if (serverText !== clientText) {
|
|
7597
|
+
out.push({
|
|
7598
|
+
kind: "text",
|
|
7599
|
+
path: nodePath,
|
|
7600
|
+
serverValue: serverText,
|
|
7601
|
+
clientValue: clientText,
|
|
7602
|
+
message: "Text content differs between server and client."
|
|
7603
|
+
});
|
|
7604
|
+
if (out.length >= max2) return;
|
|
7605
|
+
}
|
|
6518
7606
|
const serverChildren = Array.from(serverNode.children);
|
|
6519
7607
|
const clientChildren = Array.from(clientNode.children);
|
|
6520
7608
|
const max22 = Math.max(serverChildren.length, clientChildren.length);
|
|
@@ -6534,10 +7622,11 @@ function buildAttrString(attrs, { allowEventHandlers = false } = {}) {
|
|
|
6534
7622
|
if (!Object.hasOwn(attrs, rawKey)) continue;
|
|
6535
7623
|
if (!isSafeAttrName(rawKey)) continue;
|
|
6536
7624
|
if (!allowEventHandlers && isEventHandlerAttr(rawKey)) continue;
|
|
7625
|
+
if (isForbiddenSsrAttr(rawKey)) continue;
|
|
6537
7626
|
const lowerKey = rawKey.toLowerCase();
|
|
6538
7627
|
let value = String(attrs[rawKey]);
|
|
6539
|
-
if (
|
|
6540
|
-
value =
|
|
7628
|
+
if (isPolicyAttr(lowerKey)) {
|
|
7629
|
+
value = sanitizeSsrAttributeValue(lowerKey, value);
|
|
6541
7630
|
if (!value) continue;
|
|
6542
7631
|
}
|
|
6543
7632
|
out.push(`${rawKey}="${escapeAttr(value)}"`);
|
|
@@ -6545,18 +7634,7 @@ function buildAttrString(attrs, { allowEventHandlers = false } = {}) {
|
|
|
6545
7634
|
return out.join(" ");
|
|
6546
7635
|
}
|
|
6547
7636
|
function isDangerousMetaRefresh(metaProps) {
|
|
6548
|
-
|
|
6549
|
-
let content;
|
|
6550
|
-
for (const k in metaProps) {
|
|
6551
|
-
const lk = k.toLowerCase();
|
|
6552
|
-
if (lk === "http-equiv") httpEquiv = metaProps[k];
|
|
6553
|
-
else if (lk === "content") content = metaProps[k];
|
|
6554
|
-
}
|
|
6555
|
-
if (typeof httpEquiv !== "string") return false;
|
|
6556
|
-
if (httpEquiv.toLowerCase() !== "refresh") return false;
|
|
6557
|
-
if (typeof content !== "string") return false;
|
|
6558
|
-
const normalized = stripControlChars(content).toLowerCase();
|
|
6559
|
-
return normalized.includes("url=javascript:") || normalized.includes("url=data:") || normalized.includes("url=vbscript:") || normalized.includes("url=blob:");
|
|
7637
|
+
return isForbiddenMetaEntry(metaProps);
|
|
6560
7638
|
}
|
|
6561
7639
|
function renderToDocument(component, options = {}) {
|
|
6562
7640
|
let content;
|
|
@@ -6565,15 +7643,8 @@ function renderToDocument(component, options = {}) {
|
|
|
6565
7643
|
} catch (err) {
|
|
6566
7644
|
content = ssrErrorComment(err);
|
|
6567
7645
|
}
|
|
6568
|
-
const metaTags = (options.meta || []).map((attrs) =>
|
|
6569
|
-
|
|
6570
|
-
const pairs = buildAttrString(attrs);
|
|
6571
|
-
return pairs ? `<meta ${pairs} />` : "";
|
|
6572
|
-
}).filter(Boolean).join("\n ");
|
|
6573
|
-
const linkTags = (options.links || []).map((attrs) => {
|
|
6574
|
-
const pairs = buildAttrString(attrs);
|
|
6575
|
-
return pairs ? `<link ${pairs} />` : "";
|
|
6576
|
-
}).filter(Boolean).join("\n ");
|
|
7646
|
+
const metaTags = (options.meta || []).map((attrs) => serializeHeadEntry("meta", attrs)).filter(Boolean).join("\n ");
|
|
7647
|
+
const linkTags = (options.links || []).map((attrs) => serializeHeadEntry("link", attrs)).filter(Boolean).join("\n ");
|
|
6577
7648
|
const scriptTags = (options.scripts || []).map((src) => {
|
|
6578
7649
|
const safe = sanitizeUrl(String(src));
|
|
6579
7650
|
if (!safe) return "";
|
|
@@ -6634,14 +7705,18 @@ async function* renderToStream(element) {
|
|
|
6634
7705
|
const rawName = attr.name;
|
|
6635
7706
|
if (!isSafeAttrName(rawName)) continue;
|
|
6636
7707
|
if (isEventHandlerAttr(rawName)) continue;
|
|
7708
|
+
if (isForbiddenSsrAttr(rawName)) continue;
|
|
6637
7709
|
const lowerName = rawName.toLowerCase();
|
|
6638
7710
|
let value = attr.value;
|
|
6639
|
-
if (
|
|
6640
|
-
value =
|
|
7711
|
+
if (isPolicyAttr(lowerName)) {
|
|
7712
|
+
value = sanitizeSsrAttributeValue(lowerName, value);
|
|
6641
7713
|
if (!value) continue;
|
|
6642
7714
|
}
|
|
6643
7715
|
openTag += ` ${rawName}="${escapeAttr(value)}"`;
|
|
6644
7716
|
}
|
|
7717
|
+
if (element.dataset && !element.dataset.sibuHydrate) {
|
|
7718
|
+
openTag += ` data-sibu-ssr="true"`;
|
|
7719
|
+
}
|
|
6645
7720
|
if (VOID_ELEMENTS.has(tag)) {
|
|
6646
7721
|
yield `${openTag} />`;
|
|
6647
7722
|
return;
|
|
@@ -6688,9 +7763,13 @@ function island(id, component) {
|
|
|
6688
7763
|
el.setAttribute("data-sibu-island", id);
|
|
6689
7764
|
return el;
|
|
6690
7765
|
}
|
|
7766
|
+
function isAlreadyHydrated(element) {
|
|
7767
|
+
return element.hasAttribute("data-sibu-hydrated");
|
|
7768
|
+
}
|
|
6691
7769
|
function hydrateIslands(container, islands) {
|
|
6692
7770
|
const markers = container.querySelectorAll("[data-sibu-island]");
|
|
6693
7771
|
for (const marker2 of Array.from(markers)) {
|
|
7772
|
+
if (isAlreadyHydrated(marker2)) continue;
|
|
6694
7773
|
const id = marker2.getAttribute("data-sibu-island") ?? "";
|
|
6695
7774
|
if (!Object.hasOwn(islands, id)) continue;
|
|
6696
7775
|
const factory = islands[id];
|
|
@@ -6698,7 +7777,7 @@ function hydrateIslands(container, islands) {
|
|
|
6698
7777
|
const clientTree = factory();
|
|
6699
7778
|
clientTree.setAttribute("data-sibu-island", id);
|
|
6700
7779
|
clientTree.setAttribute("data-sibu-hydrated", "true");
|
|
6701
|
-
marker2
|
|
7780
|
+
disposeAndReplace(marker2, clientTree);
|
|
6702
7781
|
}
|
|
6703
7782
|
container.setAttribute("data-sibu-hydrated", "partial");
|
|
6704
7783
|
}
|
|
@@ -6706,6 +7785,7 @@ function hydrateProgressively(container, islands, options) {
|
|
|
6706
7785
|
const markers = container.querySelectorAll("[data-sibu-island]");
|
|
6707
7786
|
const cleanups = [];
|
|
6708
7787
|
for (const marker2 of Array.from(markers)) {
|
|
7788
|
+
if (isAlreadyHydrated(marker2)) continue;
|
|
6709
7789
|
const id = marker2.getAttribute("data-sibu-island") ?? "";
|
|
6710
7790
|
if (!Object.hasOwn(islands, id)) continue;
|
|
6711
7791
|
const factory = islands[id];
|
|
@@ -6715,10 +7795,11 @@ function hydrateProgressively(container, islands, options) {
|
|
|
6715
7795
|
for (const entry of entries) {
|
|
6716
7796
|
if (entry.isIntersecting) {
|
|
6717
7797
|
observer.disconnect();
|
|
7798
|
+
if (isAlreadyHydrated(marker2) || marker2.parentNode === null) break;
|
|
6718
7799
|
const clientTree = factory();
|
|
6719
7800
|
clientTree.setAttribute("data-sibu-island", id);
|
|
6720
7801
|
clientTree.setAttribute("data-sibu-hydrated", "true");
|
|
6721
|
-
marker2
|
|
7802
|
+
disposeAndReplace(marker2, clientTree);
|
|
6722
7803
|
break;
|
|
6723
7804
|
}
|
|
6724
7805
|
}
|
|
@@ -6774,7 +7855,7 @@ function suspenseSwapScript(id, nonce) {
|
|
|
6774
7855
|
);
|
|
6775
7856
|
}
|
|
6776
7857
|
const nonceAttr = nonce ? ` nonce="${escapeAttr(nonce)}"` : "";
|
|
6777
|
-
return `<script${nonceAttr}>(function(){var t=document.getElementById("sibu-resolved-${id}");var f=document.querySelector('[data-sibu-suspense-id="${id}"]');if(t&&f){while(t.firstChild)f.appendChild(t.firstChild);t.remove();f.removeAttribute("data-sibu-suspense-id");}})()</script>`;
|
|
7858
|
+
return `<script${nonceAttr}>(function(){var t=document.getElementById("sibu-resolved-${id}");var f=document.querySelector('[data-sibu-suspense-id="${id}"]');if(t&&f){while(f.firstChild)f.removeChild(f.firstChild);while(t.firstChild)f.appendChild(t.firstChild);t.remove();f.removeAttribute("data-sibu-suspense-id");}})()</script>`;
|
|
6778
7859
|
}
|
|
6779
7860
|
async function* renderToSuspenseStream(element, pendingBoundaries = [], options) {
|
|
6780
7861
|
yield* renderToStream(element);
|
|
@@ -6792,8 +7873,8 @@ function escapeScriptJson(json) {
|
|
|
6792
7873
|
return json.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
6793
7874
|
}
|
|
6794
7875
|
var DEFAULT_MAX_SSR_BYTES = 1024 * 1024;
|
|
6795
|
-
function serializeState(
|
|
6796
|
-
const rawJson = JSON.stringify(
|
|
7876
|
+
function serializeState(state2, nonce, options) {
|
|
7877
|
+
const rawJson = JSON.stringify(state2);
|
|
6797
7878
|
const maxBytes = options?.maxBytes ?? DEFAULT_MAX_SSR_BYTES;
|
|
6798
7879
|
const byteLen = typeof TextEncoder !== "undefined" ? new TextEncoder().encode(rawJson).byteLength : Buffer.byteLength(rawJson, "utf8");
|
|
6799
7880
|
if (byteLen > maxBytes) {
|
|
@@ -6855,8 +7936,9 @@ function normalizedStore(schema) {
|
|
|
6855
7936
|
ids: [],
|
|
6856
7937
|
entities: {}
|
|
6857
7938
|
});
|
|
7939
|
+
const idOf = (entity) => String(entity[idKey]);
|
|
6858
7940
|
function add(entity) {
|
|
6859
|
-
const id =
|
|
7941
|
+
const id = idOf(entity);
|
|
6860
7942
|
setState((prev) => {
|
|
6861
7943
|
const ids = prev.ids.includes(id) ? prev.ids : [...prev.ids, id];
|
|
6862
7944
|
return {
|
|
@@ -6870,7 +7952,7 @@ function normalizedStore(schema) {
|
|
|
6870
7952
|
const nextIds = [...prev.ids];
|
|
6871
7953
|
const nextEntities = { ...prev.entities };
|
|
6872
7954
|
for (const entity of entities) {
|
|
6873
|
-
const id =
|
|
7955
|
+
const id = idOf(entity);
|
|
6874
7956
|
if (!nextIds.includes(id)) {
|
|
6875
7957
|
nextIds.push(id);
|
|
6876
7958
|
}
|
|
@@ -6880,12 +7962,12 @@ function normalizedStore(schema) {
|
|
|
6880
7962
|
});
|
|
6881
7963
|
}
|
|
6882
7964
|
function get(id) {
|
|
6883
|
-
const
|
|
6884
|
-
return
|
|
7965
|
+
const state2 = getState();
|
|
7966
|
+
return state2.entities[id];
|
|
6885
7967
|
}
|
|
6886
7968
|
function getAll() {
|
|
6887
|
-
const
|
|
6888
|
-
return
|
|
7969
|
+
const state2 = getState();
|
|
7970
|
+
return state2.ids.map((id) => state2.entities[id]);
|
|
6889
7971
|
}
|
|
6890
7972
|
function update(id, partial) {
|
|
6891
7973
|
setState((prev) => {
|
|
@@ -6911,8 +7993,8 @@ function normalizedStore(schema) {
|
|
|
6911
7993
|
});
|
|
6912
7994
|
}
|
|
6913
7995
|
function select3(predicate) {
|
|
6914
|
-
const
|
|
6915
|
-
return
|
|
7996
|
+
const state2 = getState();
|
|
7997
|
+
return state2.ids.map((id) => state2.entities[id]).filter(predicate);
|
|
6916
7998
|
}
|
|
6917
7999
|
return { add, addMany, get, getAll, update, remove, select: select3, getState };
|
|
6918
8000
|
}
|
|
@@ -6992,7 +8074,14 @@ function createChunkRegistry(config = {}) {
|
|
|
6992
8074
|
} = config;
|
|
6993
8075
|
const cache = /* @__PURE__ */ new Map();
|
|
6994
8076
|
const pending = /* @__PURE__ */ new Map();
|
|
6995
|
-
const preloaded = /* @__PURE__ */ new
|
|
8077
|
+
const preloaded = /* @__PURE__ */ new Map();
|
|
8078
|
+
function notify(name, run) {
|
|
8079
|
+
try {
|
|
8080
|
+
run();
|
|
8081
|
+
} catch (err) {
|
|
8082
|
+
reportError(err, { phase: "async", name: `chunkRegistry(${name})` });
|
|
8083
|
+
}
|
|
8084
|
+
}
|
|
6996
8085
|
function evict() {
|
|
6997
8086
|
while (cache.size >= maxCacheSize) {
|
|
6998
8087
|
let lru = null;
|
|
@@ -7047,30 +8136,47 @@ function createChunkRegistry(config = {}) {
|
|
|
7047
8136
|
cached.lastAccess = Date.now();
|
|
7048
8137
|
return cached.value;
|
|
7049
8138
|
}
|
|
7050
|
-
const
|
|
7051
|
-
if (
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
pending.delete(id);
|
|
7058
|
-
onLoadEnd?.(id);
|
|
7059
|
-
return value;
|
|
7060
|
-
}).catch((err) => {
|
|
7061
|
-
pending.delete(id);
|
|
7062
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
7063
|
-
onLoadError?.(id, error);
|
|
7064
|
-
throw error;
|
|
8139
|
+
const existing = pending.get(id);
|
|
8140
|
+
if (existing) return existing.promise;
|
|
8141
|
+
let resolveOuter;
|
|
8142
|
+
let rejectOuter;
|
|
8143
|
+
const outcome = new Promise((resolve, reject) => {
|
|
8144
|
+
resolveOuter = resolve;
|
|
8145
|
+
rejectOuter = reject;
|
|
7065
8146
|
});
|
|
7066
|
-
|
|
7067
|
-
|
|
8147
|
+
const entry = { promise: outcome };
|
|
8148
|
+
pending.set(id, entry);
|
|
8149
|
+
const owns = () => pending.get(id) === entry;
|
|
8150
|
+
notify("onLoadStart", () => onLoadStart?.(id));
|
|
8151
|
+
void loadWithRetry(id, loader).then(
|
|
8152
|
+
(value) => {
|
|
8153
|
+
if (owns()) {
|
|
8154
|
+
pending.delete(id);
|
|
8155
|
+
evict();
|
|
8156
|
+
const now = Date.now();
|
|
8157
|
+
cache.set(id, { value, timestamp: now, lastAccess: now, accessCount: 1 });
|
|
8158
|
+
}
|
|
8159
|
+
notify("onLoadEnd", () => onLoadEnd?.(id));
|
|
8160
|
+
resolveOuter(value);
|
|
8161
|
+
},
|
|
8162
|
+
(err) => {
|
|
8163
|
+
if (owns()) pending.delete(id);
|
|
8164
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
8165
|
+
notify("onLoadError", () => onLoadError?.(id, error));
|
|
8166
|
+
rejectOuter(error);
|
|
8167
|
+
}
|
|
8168
|
+
).catch((internal) => {
|
|
8169
|
+
reportError(internal, { phase: "async", name: "chunkRegistry(settlement)" });
|
|
8170
|
+
rejectOuter(internal);
|
|
8171
|
+
});
|
|
8172
|
+
return outcome;
|
|
7068
8173
|
}
|
|
7069
8174
|
function preloadFn(id, loader) {
|
|
7070
8175
|
if (cache.has(id) || pending.has(id) || preloaded.has(id)) return;
|
|
7071
|
-
|
|
8176
|
+
const marker2 = {};
|
|
8177
|
+
preloaded.set(id, marker2);
|
|
7072
8178
|
loadFn(id, loader).catch(() => {
|
|
7073
|
-
preloaded.delete(id);
|
|
8179
|
+
if (preloaded.get(id) === marker2) preloaded.delete(id);
|
|
7074
8180
|
});
|
|
7075
8181
|
}
|
|
7076
8182
|
return {
|
|
@@ -7104,21 +8210,33 @@ function createChunkRegistry(config = {}) {
|
|
|
7104
8210
|
return void 0;
|
|
7105
8211
|
},
|
|
7106
8212
|
/**
|
|
7107
|
-
* Invalidate a cached chunk.
|
|
8213
|
+
* Invalidate a cached chunk, and supersede any load still in flight for it.
|
|
8214
|
+
*
|
|
8215
|
+
* In-flight work is not cancelled — the loader API takes no abort signal, so
|
|
8216
|
+
* claiming cancellation would be a lie. It is *superseded*: it still settles
|
|
8217
|
+
* for whoever called `load()`, but it can no longer write to the cache, and
|
|
8218
|
+
* a subsequent `load(id, …)` starts fresh work rather than adopting it.
|
|
7108
8219
|
*/
|
|
7109
8220
|
invalidate(id) {
|
|
7110
8221
|
cache.delete(id);
|
|
7111
8222
|
preloaded.delete(id);
|
|
8223
|
+
pending.delete(id);
|
|
7112
8224
|
},
|
|
7113
8225
|
/**
|
|
7114
|
-
* Clear all cached chunks.
|
|
8226
|
+
* Clear all cached chunks, and supersede every load still in flight.
|
|
8227
|
+
*
|
|
8228
|
+
* Same barrier as `invalidate`, applied to every key at once.
|
|
7115
8229
|
*/
|
|
7116
8230
|
clear() {
|
|
7117
8231
|
cache.clear();
|
|
7118
8232
|
preloaded.clear();
|
|
8233
|
+
pending.clear();
|
|
7119
8234
|
},
|
|
7120
8235
|
/**
|
|
7121
8236
|
* Get cache statistics.
|
|
8237
|
+
*
|
|
8238
|
+
* Describes currently OWNED state: superseded loads are already absent from
|
|
8239
|
+
* `pending`, so the counts never include work whose result will be discarded.
|
|
7122
8240
|
*/
|
|
7123
8241
|
stats() {
|
|
7124
8242
|
return {
|
|
@@ -7171,38 +8289,57 @@ function preloadModules(urls) {
|
|
|
7171
8289
|
}
|
|
7172
8290
|
|
|
7173
8291
|
// src/platform/head.ts
|
|
7174
|
-
var HEAD_URL_ATTRS = /* @__PURE__ */ new Set(["href", "src"]);
|
|
7175
|
-
function sanitizeHeadAttr(key, value) {
|
|
7176
|
-
if (HEAD_URL_ATTRS.has(key)) return sanitizeUrl(value);
|
|
7177
|
-
return value;
|
|
7178
|
-
}
|
|
7179
|
-
function isDangerousRefreshContent(content) {
|
|
7180
|
-
const normalized = stripControlChars(content).toLowerCase();
|
|
7181
|
-
return normalized.includes("url=javascript:") || normalized.includes("url=data:") || normalized.includes("url=vbscript:") || normalized.includes("url=blob:");
|
|
7182
|
-
}
|
|
7183
|
-
function getMetaAttr(metaProps, name) {
|
|
7184
|
-
for (const k in metaProps) {
|
|
7185
|
-
if (k.toLowerCase() === name) return metaProps[k];
|
|
7186
|
-
}
|
|
7187
|
-
return void 0;
|
|
7188
|
-
}
|
|
7189
|
-
function isDangerousMetaRefresh2(metaProps) {
|
|
7190
|
-
const httpEquiv = getMetaAttr(metaProps, "http-equiv");
|
|
7191
|
-
if (typeof httpEquiv !== "string") return false;
|
|
7192
|
-
if (httpEquiv.toLowerCase() !== "refresh") return false;
|
|
7193
|
-
const content = getMetaAttr(metaProps, "content");
|
|
7194
|
-
if (typeof content !== "string") return false;
|
|
7195
|
-
return isDangerousRefreshContent(content);
|
|
7196
|
-
}
|
|
7197
|
-
var SAFE_HEAD_ATTR_NAME = /^[A-Za-z_:][-A-Za-z0-9_.:]*$/;
|
|
7198
|
-
function isSafeHeadAttr(name) {
|
|
7199
|
-
if (!SAFE_HEAD_ATTR_NAME.test(name)) return false;
|
|
7200
|
-
if (isEventHandlerAttr(name)) return false;
|
|
7201
|
-
return true;
|
|
7202
|
-
}
|
|
7203
8292
|
function escapeScriptJsonLocal(json) {
|
|
7204
8293
|
return json.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
7205
8294
|
}
|
|
8295
|
+
var CLIENT_RESOLVER = {
|
|
8296
|
+
resolveValue: (_name, value) => typeof value === "function" ? String(value()) : String(value),
|
|
8297
|
+
isReactiveValue: (value) => typeof value === "function"
|
|
8298
|
+
};
|
|
8299
|
+
function applyMetaEntry(metaProps, managedElements) {
|
|
8300
|
+
let current = null;
|
|
8301
|
+
const retract = () => {
|
|
8302
|
+
if (!current) return;
|
|
8303
|
+
const index = managedElements.indexOf(current);
|
|
8304
|
+
if (index !== -1) managedElements.splice(index, 1);
|
|
8305
|
+
current.remove();
|
|
8306
|
+
current = null;
|
|
8307
|
+
};
|
|
8308
|
+
const alreadyPublished = (attributes) => {
|
|
8309
|
+
if (!current || current.attributes.length !== attributes.size) return false;
|
|
8310
|
+
for (const [name, value] of attributes) {
|
|
8311
|
+
if (current.getAttribute(name) !== value) return false;
|
|
8312
|
+
}
|
|
8313
|
+
return true;
|
|
8314
|
+
};
|
|
8315
|
+
const publish = (attributes) => {
|
|
8316
|
+
if (alreadyPublished(attributes)) return;
|
|
8317
|
+
const next = document.createElement("meta");
|
|
8318
|
+
for (const [name, value] of attributes) next.setAttribute(name, value);
|
|
8319
|
+
const index = current ? managedElements.indexOf(current) : -1;
|
|
8320
|
+
if (current?.parentNode) {
|
|
8321
|
+
current.replaceWith(next);
|
|
8322
|
+
} else {
|
|
8323
|
+
current?.remove();
|
|
8324
|
+
document.head.appendChild(next);
|
|
8325
|
+
}
|
|
8326
|
+
if (index !== -1) managedElements[index] = next;
|
|
8327
|
+
else managedElements.push(next);
|
|
8328
|
+
current = next;
|
|
8329
|
+
};
|
|
8330
|
+
const stopEffect = effect(() => {
|
|
8331
|
+
const plan = planMetaEntry(metaProps, CLIENT_RESOLVER);
|
|
8332
|
+
if (plan.kind === "drop") {
|
|
8333
|
+
retract();
|
|
8334
|
+
return;
|
|
8335
|
+
}
|
|
8336
|
+
publish(plan.attributes);
|
|
8337
|
+
});
|
|
8338
|
+
return () => {
|
|
8339
|
+
stopEffect();
|
|
8340
|
+
retract();
|
|
8341
|
+
};
|
|
8342
|
+
}
|
|
7206
8343
|
function Head(props) {
|
|
7207
8344
|
const anchor = document.createComment("sibu-head");
|
|
7208
8345
|
const managedElements = [];
|
|
@@ -7219,78 +8356,52 @@ function Head(props) {
|
|
|
7219
8356
|
cleanup3();
|
|
7220
8357
|
if (props.title) {
|
|
7221
8358
|
if (typeof props.title === "function") {
|
|
7222
|
-
const
|
|
7223
|
-
|
|
8359
|
+
const getter = props.title;
|
|
8360
|
+
let lease = null;
|
|
8361
|
+
const stopEffect = effect(() => {
|
|
8362
|
+
const next = getter();
|
|
8363
|
+
if (lease) lease.set(next);
|
|
8364
|
+
else lease = acquireTitle(next);
|
|
8365
|
+
});
|
|
8366
|
+
effectCleanups.push(() => {
|
|
8367
|
+
stopEffect();
|
|
8368
|
+
lease?.release();
|
|
8369
|
+
lease = null;
|
|
7224
8370
|
});
|
|
7225
|
-
effectCleanups.push(cleanupFn);
|
|
7226
8371
|
} else {
|
|
7227
|
-
|
|
8372
|
+
const lease = acquireTitle(props.title);
|
|
8373
|
+
effectCleanups.push(() => lease.release());
|
|
7228
8374
|
}
|
|
7229
8375
|
}
|
|
7230
8376
|
if (props.meta) {
|
|
7231
8377
|
for (const metaProps of props.meta) {
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
el.removeAttribute(key);
|
|
7247
|
-
return;
|
|
7248
|
-
}
|
|
7249
|
-
el.setAttribute(key, sanitizeHeadAttr(key, resolved));
|
|
7250
|
-
});
|
|
7251
|
-
effectCleanups.push(cleanupFn);
|
|
7252
|
-
} else {
|
|
7253
|
-
if (isContent && isRefreshNow() && isDangerousRefreshContent(value)) continue;
|
|
7254
|
-
el.setAttribute(key, sanitizeHeadAttr(key, value));
|
|
7255
|
-
}
|
|
7256
|
-
}
|
|
7257
|
-
document.head.appendChild(el);
|
|
7258
|
-
managedElements.push(el);
|
|
7259
|
-
}
|
|
7260
|
-
}
|
|
7261
|
-
if (props.link) {
|
|
7262
|
-
for (const linkProps of props.link) {
|
|
7263
|
-
const el = document.createElement("link");
|
|
7264
|
-
for (const [key, value] of Object.entries(linkProps)) {
|
|
7265
|
-
if (!isSafeHeadAttr(key)) continue;
|
|
7266
|
-
el.setAttribute(key, sanitizeHeadAttr(key, value));
|
|
7267
|
-
}
|
|
7268
|
-
document.head.appendChild(el);
|
|
7269
|
-
managedElements.push(el);
|
|
7270
|
-
}
|
|
7271
|
-
}
|
|
7272
|
-
if (props.script) {
|
|
7273
|
-
for (const scriptProps of props.script) {
|
|
7274
|
-
const el = document.createElement("script");
|
|
7275
|
-
for (const [key, value] of Object.entries(scriptProps)) {
|
|
7276
|
-
if (!isSafeHeadAttr(key)) continue;
|
|
7277
|
-
el.setAttribute(key, sanitizeHeadAttr(key, value));
|
|
7278
|
-
}
|
|
8378
|
+
const stop2 = applyMetaEntry(metaProps, managedElements);
|
|
8379
|
+
effectCleanups.push(stop2);
|
|
8380
|
+
}
|
|
8381
|
+
}
|
|
8382
|
+
for (const [tag, entries] of [
|
|
8383
|
+
["link", props.link],
|
|
8384
|
+
["script", props.script]
|
|
8385
|
+
]) {
|
|
8386
|
+
if (!entries) continue;
|
|
8387
|
+
for (const entryProps of entries) {
|
|
8388
|
+
const plan = planHeadElementEntry(entryProps, STATIC_VALUE_RESOLVER);
|
|
8389
|
+
if (plan.kind === "drop") continue;
|
|
8390
|
+
const el = document.createElement(tag);
|
|
8391
|
+
for (const [name, value] of plan.attributes) el.setAttribute(name, value);
|
|
7279
8392
|
document.head.appendChild(el);
|
|
7280
8393
|
managedElements.push(el);
|
|
7281
8394
|
}
|
|
7282
8395
|
}
|
|
7283
8396
|
if (props.base) {
|
|
7284
|
-
const
|
|
7285
|
-
if (existing) existing.remove();
|
|
7286
|
-
const el = document.createElement("base");
|
|
8397
|
+
const spec = {};
|
|
7287
8398
|
if (props.base.href) {
|
|
7288
8399
|
const safeHref = sanitizeUrl(props.base.href);
|
|
7289
|
-
if (safeHref)
|
|
8400
|
+
if (safeHref) spec.href = safeHref;
|
|
7290
8401
|
}
|
|
7291
|
-
if (props.base.target)
|
|
7292
|
-
|
|
7293
|
-
|
|
8402
|
+
if (props.base.target) spec.target = props.base.target;
|
|
8403
|
+
const lease = acquireBase(spec);
|
|
8404
|
+
effectCleanups.push(() => lease.release());
|
|
7294
8405
|
}
|
|
7295
8406
|
};
|
|
7296
8407
|
apply();
|
|
@@ -7313,10 +8424,13 @@ function setCanonical(url) {
|
|
|
7313
8424
|
link2.rel = "canonical";
|
|
7314
8425
|
document.head.appendChild(link2);
|
|
7315
8426
|
}
|
|
7316
|
-
|
|
8427
|
+
const safe = sanitizeUrl(url);
|
|
8428
|
+
if (safe) link2.setAttribute("href", safe);
|
|
8429
|
+
else link2.removeAttribute("href");
|
|
7317
8430
|
}
|
|
7318
8431
|
|
|
7319
8432
|
// src/platform/customElement.ts
|
|
8433
|
+
var SVG_NS2 = "http://www.w3.org/2000/svg";
|
|
7320
8434
|
function defineElement(name, component, options = {}) {
|
|
7321
8435
|
if (customElements.get(name)) return;
|
|
7322
8436
|
const observed = options.observedAttributes || [];
|
|
@@ -7345,11 +8459,8 @@ function defineElement(name, component, options = {}) {
|
|
|
7345
8459
|
}
|
|
7346
8460
|
}
|
|
7347
8461
|
_teardown() {
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
this._rendered = null;
|
|
7351
|
-
}
|
|
7352
|
-
this._root.replaceChildren();
|
|
8462
|
+
this._rendered = null;
|
|
8463
|
+
replaceChildrenSafely(this._root);
|
|
7353
8464
|
}
|
|
7354
8465
|
_render() {
|
|
7355
8466
|
this._teardown();
|
|
@@ -7374,14 +8485,13 @@ function defineElement(name, component, options = {}) {
|
|
|
7374
8485
|
customElements.define(name, SibuElement);
|
|
7375
8486
|
}
|
|
7376
8487
|
function svgElement(tag, props = {}, ...nodes) {
|
|
7377
|
-
const SVG_NS2 = "http://www.w3.org/2000/svg";
|
|
7378
8488
|
const el = document.createElementNS(SVG_NS2, tag);
|
|
7379
8489
|
for (const [key, value] of Object.entries(props)) {
|
|
7380
8490
|
if (key === "nodes") continue;
|
|
7381
|
-
if (typeof value === "function" && key
|
|
8491
|
+
if (typeof value === "function" && isEventHandlerAttr(key)) {
|
|
7382
8492
|
el.addEventListener(key.slice(2).toLowerCase(), value);
|
|
7383
8493
|
} else if (value != null) {
|
|
7384
|
-
el
|
|
8494
|
+
setSafeAttribute(el, key, value, { label: "svgElement" });
|
|
7385
8495
|
}
|
|
7386
8496
|
}
|
|
7387
8497
|
for (const child of nodes) {
|
|
@@ -7617,22 +8727,34 @@ var instanceCache = globalSingleton(
|
|
|
7617
8727
|
/* @__PURE__ */ Symbol.for("sibujs.wasm.instanceCache.v1"),
|
|
7618
8728
|
() => /* @__PURE__ */ new Map()
|
|
7619
8729
|
);
|
|
8730
|
+
var inFlightCache = globalSingleton(
|
|
8731
|
+
/* @__PURE__ */ Symbol.for("sibujs.wasm.inFlightCache.v1"),
|
|
8732
|
+
() => /* @__PURE__ */ new Map()
|
|
8733
|
+
);
|
|
8734
|
+
var cacheState = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.wasm.cacheState.v1"), () => ({ generation: 0 }));
|
|
8735
|
+
function isCurrentGeneration(generation) {
|
|
8736
|
+
return generation === cacheState.generation;
|
|
8737
|
+
}
|
|
7620
8738
|
function wasm(source2, config = {}) {
|
|
7621
8739
|
const [instance, setInstance] = signal(null);
|
|
7622
8740
|
const [loading, setLoading] = signal(true);
|
|
7623
8741
|
const [error, setError] = signal(null);
|
|
7624
8742
|
const cacheKey = config.cacheKey || (typeof source2 === "string" ? source2 : void 0);
|
|
8743
|
+
let runId = 0;
|
|
7625
8744
|
async function load() {
|
|
8745
|
+
const myRun = ++runId;
|
|
7626
8746
|
setLoading(true);
|
|
7627
8747
|
setError(null);
|
|
7628
8748
|
setInstance(null);
|
|
7629
8749
|
try {
|
|
7630
|
-
const wasmInstance = await
|
|
8750
|
+
const wasmInstance = await loadWasmModuleWithOptions(source2, { ...config, cacheKey });
|
|
8751
|
+
if (myRun !== runId) return;
|
|
7631
8752
|
setInstance(wasmInstance.exports);
|
|
7632
8753
|
} catch (err) {
|
|
8754
|
+
if (myRun !== runId) return;
|
|
7633
8755
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
7634
8756
|
} finally {
|
|
7635
|
-
setLoading(false);
|
|
8757
|
+
if (myRun === runId) setLoading(false);
|
|
7636
8758
|
}
|
|
7637
8759
|
}
|
|
7638
8760
|
load();
|
|
@@ -7645,9 +8767,9 @@ function wasm(source2, config = {}) {
|
|
|
7645
8767
|
};
|
|
7646
8768
|
}
|
|
7647
8769
|
async function loadWasmModule(source2, imports, cacheKey) {
|
|
7648
|
-
|
|
7649
|
-
|
|
7650
|
-
|
|
8770
|
+
return loadWasmModuleWithOptions(source2, { imports, cacheKey });
|
|
8771
|
+
}
|
|
8772
|
+
async function loadWasmModuleWithOptions(source2, opts = {}) {
|
|
7651
8773
|
const key = opts.cacheKey || (typeof source2 === "string" ? source2 : void 0);
|
|
7652
8774
|
if (typeof source2 === "string") {
|
|
7653
8775
|
const allowed = opts.allowedOrigins ?? [];
|
|
@@ -7667,44 +8789,50 @@ async function loadWasmModule(source2, imports, cacheKey) {
|
|
|
7667
8789
|
);
|
|
7668
8790
|
}
|
|
7669
8791
|
}
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
8792
|
+
const generation = cacheState.generation;
|
|
8793
|
+
if (!key) return instantiateWasm(source2, opts.imports, void 0, generation);
|
|
8794
|
+
const cachedInstance = instanceCache.get(key);
|
|
8795
|
+
if (cachedInstance) return cachedInstance;
|
|
8796
|
+
const inFlight = inFlightCache.get(key);
|
|
8797
|
+
if (inFlight) return inFlight;
|
|
8798
|
+
const pending = instantiateWasm(source2, opts.imports, key, generation).then((instance) => {
|
|
8799
|
+
if (isCurrentGeneration(generation)) instanceCache.set(key, instance);
|
|
8800
|
+
return instance;
|
|
8801
|
+
});
|
|
8802
|
+
inFlightCache.set(key, pending);
|
|
8803
|
+
void pending.catch(() => {
|
|
8804
|
+
}).then(() => {
|
|
8805
|
+
if (inFlightCache.get(key) === pending) inFlightCache.delete(key);
|
|
8806
|
+
});
|
|
8807
|
+
return pending;
|
|
8808
|
+
}
|
|
8809
|
+
async function instantiateWasm(source2, wasmImports, key, generation) {
|
|
7677
8810
|
const cachedModule = key ? moduleCache.get(key) : void 0;
|
|
7678
8811
|
if (cachedModule) {
|
|
7679
|
-
|
|
8812
|
+
return WebAssembly.instantiate(cachedModule, wasmImports || {});
|
|
8813
|
+
}
|
|
8814
|
+
let bytes;
|
|
8815
|
+
if (typeof source2 === "string") {
|
|
8816
|
+
if (typeof WebAssembly.instantiateStreaming === "function") {
|
|
8817
|
+
const response2 = fetch(source2);
|
|
8818
|
+
const result = await WebAssembly.instantiateStreaming(response2, wasmImports || {});
|
|
8819
|
+
if (key && isCurrentGeneration(generation)) moduleCache.set(key, result.module);
|
|
8820
|
+
return result.instance;
|
|
8821
|
+
}
|
|
8822
|
+
const response = await fetch(source2);
|
|
8823
|
+
bytes = await response.arrayBuffer();
|
|
8824
|
+
} else if (source2 instanceof Uint8Array) {
|
|
8825
|
+
bytes = source2.buffer.slice(source2.byteOffset, source2.byteOffset + source2.byteLength);
|
|
7680
8826
|
} else {
|
|
7681
|
-
|
|
7682
|
-
if (typeof source2 === "string") {
|
|
7683
|
-
if (typeof WebAssembly.instantiateStreaming === "function") {
|
|
7684
|
-
const response2 = fetch(source2);
|
|
7685
|
-
const result = await WebAssembly.instantiateStreaming(response2, wasmImports || {});
|
|
7686
|
-
if (key) {
|
|
7687
|
-
moduleCache.set(key, result.module);
|
|
7688
|
-
instanceCache.set(key, result.instance);
|
|
7689
|
-
}
|
|
7690
|
-
return result.instance;
|
|
7691
|
-
}
|
|
7692
|
-
const response = await fetch(source2);
|
|
7693
|
-
bytes = await response.arrayBuffer();
|
|
7694
|
-
} else if (source2 instanceof Uint8Array) {
|
|
7695
|
-
bytes = source2.buffer.slice(source2.byteOffset, source2.byteOffset + source2.byteLength);
|
|
7696
|
-
} else {
|
|
7697
|
-
bytes = source2;
|
|
7698
|
-
}
|
|
7699
|
-
module2 = await WebAssembly.compile(bytes);
|
|
7700
|
-
if (key) moduleCache.set(key, module2);
|
|
8827
|
+
bytes = source2;
|
|
7701
8828
|
}
|
|
7702
|
-
const
|
|
7703
|
-
if (key)
|
|
7704
|
-
return
|
|
8829
|
+
const module2 = await WebAssembly.compile(bytes);
|
|
8830
|
+
if (key && isCurrentGeneration(generation)) moduleCache.set(key, module2);
|
|
8831
|
+
return WebAssembly.instantiate(module2, wasmImports || {});
|
|
7705
8832
|
}
|
|
7706
8833
|
async function preloadWasm(url, options = {}) {
|
|
7707
8834
|
if (moduleCache.has(url)) return;
|
|
8835
|
+
const generation = cacheState.generation;
|
|
7708
8836
|
const allowed = options.allowedOrigins ?? [];
|
|
7709
8837
|
if (allowed.length > 0) {
|
|
7710
8838
|
let parsed;
|
|
@@ -7729,7 +8857,7 @@ async function preloadWasm(url, options = {}) {
|
|
|
7729
8857
|
const bytes = await response.arrayBuffer();
|
|
7730
8858
|
module2 = await WebAssembly.compile(bytes);
|
|
7731
8859
|
}
|
|
7732
|
-
moduleCache.set(url, module2);
|
|
8860
|
+
if (isCurrentGeneration(generation)) moduleCache.set(url, module2);
|
|
7733
8861
|
}
|
|
7734
8862
|
function createWasmBridge(instance) {
|
|
7735
8863
|
const exports2 = instance.exports;
|
|
@@ -7769,8 +8897,10 @@ function createWasmBridge(instance) {
|
|
|
7769
8897
|
};
|
|
7770
8898
|
}
|
|
7771
8899
|
function clearWasmCache() {
|
|
8900
|
+
cacheState.generation++;
|
|
7772
8901
|
moduleCache.clear();
|
|
7773
8902
|
instanceCache.clear();
|
|
8903
|
+
inFlightCache.clear();
|
|
7774
8904
|
}
|
|
7775
8905
|
function isWasmCached(key) {
|
|
7776
8906
|
return moduleCache.has(key);
|
|
@@ -7785,7 +8915,7 @@ function bindChildNode(placeholder, getter) {
|
|
|
7785
8915
|
try {
|
|
7786
8916
|
result = getter();
|
|
7787
8917
|
} catch (err) {
|
|
7788
|
-
|
|
8918
|
+
reportError(err, { phase: "binding", name: "bindChildNode", node: placeholder });
|
|
7789
8919
|
return;
|
|
7790
8920
|
}
|
|
7791
8921
|
if (result == null || typeof result === "boolean") {
|
|
@@ -7846,11 +8976,11 @@ function bindChildNode(placeholder, getter) {
|
|
|
7846
8976
|
}
|
|
7847
8977
|
lastNodes = newNodes;
|
|
7848
8978
|
}
|
|
7849
|
-
return reactiveBinding2(commit);
|
|
8979
|
+
return reactiveBinding2(commit, placeholder);
|
|
7850
8980
|
}
|
|
7851
8981
|
|
|
7852
8982
|
// src/core/rendering/tagFactory.ts
|
|
7853
|
-
var
|
|
8983
|
+
var SVG_NS3 = "http://www.w3.org/2000/svg";
|
|
7854
8984
|
var _isDev10 = isDev();
|
|
7855
8985
|
var BLOCKED_TAGS = /* @__PURE__ */ new Set(["script", "iframe", "object", "embed", "frame", "frameset"]);
|
|
7856
8986
|
function isBlockedTag(tag) {
|
|
@@ -7869,9 +8999,6 @@ var CLOBBER_RISKY_IDS = /* @__PURE__ */ new Set([
|
|
|
7869
8999
|
"self",
|
|
7870
9000
|
"frames"
|
|
7871
9001
|
]);
|
|
7872
|
-
function setProp2(el, key, val) {
|
|
7873
|
-
el[key] = val;
|
|
7874
|
-
}
|
|
7875
9002
|
function looksLikeClassList(s2) {
|
|
7876
9003
|
const t = s2.trim();
|
|
7877
9004
|
if (!t) return false;
|
|
@@ -7895,13 +9022,13 @@ function toKebab(prop) {
|
|
|
7895
9022
|
function applyStyle(el, style2) {
|
|
7896
9023
|
if (typeof style2 === "function") {
|
|
7897
9024
|
const teardown = track2(() => {
|
|
7898
|
-
el.setAttribute("style", style2());
|
|
9025
|
+
el.setAttribute("style", sanitizeStyleAttribute(String(style2())));
|
|
7899
9026
|
});
|
|
7900
9027
|
registerDisposer(el, teardown);
|
|
7901
9028
|
return;
|
|
7902
9029
|
}
|
|
7903
9030
|
if (typeof style2 === "string") {
|
|
7904
|
-
el.setAttribute("style", style2);
|
|
9031
|
+
el.setAttribute("style", sanitizeStyleAttribute(style2));
|
|
7905
9032
|
return;
|
|
7906
9033
|
}
|
|
7907
9034
|
const htmlEl = el;
|
|
@@ -8086,16 +9213,8 @@ var tagFactory = (tag, ns) => {
|
|
|
8086
9213
|
if (isEventHandlerAttr(key)) continue;
|
|
8087
9214
|
if (typeof value === "function") {
|
|
8088
9215
|
registerDisposer(el, bindAttribute(el, key, value));
|
|
8089
|
-
} else if (typeof value === "boolean") {
|
|
8090
|
-
if (key in el && (key === "checked" || key === "disabled" || key === "selected")) {
|
|
8091
|
-
setProp2(el, key, value);
|
|
8092
|
-
} else if (value) {
|
|
8093
|
-
el.setAttribute(key, "");
|
|
8094
|
-
} else {
|
|
8095
|
-
el.removeAttribute(key);
|
|
8096
|
-
}
|
|
8097
9216
|
} else {
|
|
8098
|
-
el
|
|
9217
|
+
setSafeAttribute(el, key, value, { syncValueProperty: false, label: "tagFactory" });
|
|
8099
9218
|
}
|
|
8100
9219
|
}
|
|
8101
9220
|
}
|
|
@@ -8180,7 +9299,7 @@ var param = tagFactory("param");
|
|
|
8180
9299
|
var picture = tagFactory("picture");
|
|
8181
9300
|
var portal = tagFactory("portal");
|
|
8182
9301
|
var source = tagFactory("source");
|
|
8183
|
-
var svg = tagFactory("svg",
|
|
9302
|
+
var svg = tagFactory("svg", SVG_NS3);
|
|
8184
9303
|
var math = tagFactory("math");
|
|
8185
9304
|
var canvas = tagFactory("canvas");
|
|
8186
9305
|
var noscript = tagFactory("noscript");
|
|
@@ -8221,26 +9340,26 @@ var base = tagFactory("base");
|
|
|
8221
9340
|
var link = tagFactory("link");
|
|
8222
9341
|
var meta = tagFactory("meta");
|
|
8223
9342
|
var style = tagFactory("style");
|
|
8224
|
-
var circle = tagFactory("circle",
|
|
8225
|
-
var ellipse = tagFactory("ellipse",
|
|
8226
|
-
var g = tagFactory("g",
|
|
8227
|
-
var line = tagFactory("line",
|
|
8228
|
-
var path = tagFactory("path",
|
|
8229
|
-
var polygon = tagFactory("polygon",
|
|
8230
|
-
var polyline = tagFactory("polyline",
|
|
8231
|
-
var rect = tagFactory("rect",
|
|
8232
|
-
var text = tagFactory("text",
|
|
8233
|
-
var tspan = tagFactory("tspan",
|
|
8234
|
-
var defs = tagFactory("defs",
|
|
8235
|
-
var clipPath = tagFactory("clipPath",
|
|
8236
|
-
var mask = tagFactory("mask",
|
|
8237
|
-
var pattern = tagFactory("pattern",
|
|
8238
|
-
var linearGradient = tagFactory("linearGradient",
|
|
8239
|
-
var radialGradient = tagFactory("radialGradient",
|
|
8240
|
-
var stop = tagFactory("stop",
|
|
8241
|
-
var use = tagFactory("use",
|
|
8242
|
-
var symbol = tagFactory("symbol",
|
|
8243
|
-
var marker = tagFactory("marker",
|
|
9343
|
+
var circle = tagFactory("circle", SVG_NS3);
|
|
9344
|
+
var ellipse = tagFactory("ellipse", SVG_NS3);
|
|
9345
|
+
var g = tagFactory("g", SVG_NS3);
|
|
9346
|
+
var line = tagFactory("line", SVG_NS3);
|
|
9347
|
+
var path = tagFactory("path", SVG_NS3);
|
|
9348
|
+
var polygon = tagFactory("polygon", SVG_NS3);
|
|
9349
|
+
var polyline = tagFactory("polyline", SVG_NS3);
|
|
9350
|
+
var rect = tagFactory("rect", SVG_NS3);
|
|
9351
|
+
var text = tagFactory("text", SVG_NS3);
|
|
9352
|
+
var tspan = tagFactory("tspan", SVG_NS3);
|
|
9353
|
+
var defs = tagFactory("defs", SVG_NS3);
|
|
9354
|
+
var clipPath = tagFactory("clipPath", SVG_NS3);
|
|
9355
|
+
var mask = tagFactory("mask", SVG_NS3);
|
|
9356
|
+
var pattern = tagFactory("pattern", SVG_NS3);
|
|
9357
|
+
var linearGradient = tagFactory("linearGradient", SVG_NS3);
|
|
9358
|
+
var radialGradient = tagFactory("radialGradient", SVG_NS3);
|
|
9359
|
+
var stop = tagFactory("stop", SVG_NS3);
|
|
9360
|
+
var use = tagFactory("use", SVG_NS3);
|
|
9361
|
+
var symbol = tagFactory("symbol", SVG_NS3);
|
|
9362
|
+
var marker = tagFactory("marker", SVG_NS3);
|
|
8244
9363
|
var center = tagFactory("center");
|
|
8245
9364
|
var font = tagFactory("font");
|
|
8246
9365
|
var marquee = tagFactory("marquee");
|
|
@@ -8261,14 +9380,13 @@ function createMicroApp(config) {
|
|
|
8261
9380
|
}
|
|
8262
9381
|
let mounted = false;
|
|
8263
9382
|
function mount(component) {
|
|
8264
|
-
root.replaceChildren();
|
|
8265
9383
|
const el = component();
|
|
8266
|
-
root
|
|
9384
|
+
replaceChildrenSafely(root, el);
|
|
8267
9385
|
mounted = true;
|
|
8268
9386
|
}
|
|
8269
9387
|
function unmount() {
|
|
8270
9388
|
if (!mounted) return;
|
|
8271
|
-
root
|
|
9389
|
+
replaceChildrenSafely(root);
|
|
8272
9390
|
mounted = false;
|
|
8273
9391
|
}
|
|
8274
9392
|
return { mount, unmount, element: host };
|
|
@@ -8315,18 +9433,21 @@ function defineRemoteComponent(name, loader) {
|
|
|
8315
9433
|
class: "sibu-remote",
|
|
8316
9434
|
"data-remote-component": name
|
|
8317
9435
|
});
|
|
8318
|
-
container.appendChild(span({ class: "sibu-remote-loading",
|
|
9436
|
+
container.appendChild(span({ class: "sibu-remote-loading" }, "Loading..."));
|
|
9437
|
+
let disposed = false;
|
|
9438
|
+
registerDisposer(container, () => {
|
|
9439
|
+
disposed = true;
|
|
9440
|
+
});
|
|
8319
9441
|
loader().then((mod) => {
|
|
8320
9442
|
cached = mod.default;
|
|
8321
|
-
|
|
8322
|
-
container
|
|
9443
|
+
if (disposed) return;
|
|
9444
|
+
replaceChildrenSafely(container, cached());
|
|
8323
9445
|
}).catch((err) => {
|
|
9446
|
+
if (disposed) return;
|
|
8324
9447
|
const message = err instanceof Error ? err.message : String(err);
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
nodes: `Failed to load remote component "${name}": ${message}`
|
|
8329
|
-
})
|
|
9448
|
+
replaceChildrenSafely(
|
|
9449
|
+
container,
|
|
9450
|
+
div({ class: "sibu-remote-error" }, `Failed to load remote component "${name}": ${message}`)
|
|
8330
9451
|
);
|
|
8331
9452
|
});
|
|
8332
9453
|
return container;
|
|
@@ -8382,11 +9503,16 @@ function serviceWorker(scriptUrl, options) {
|
|
|
8382
9503
|
const [isReady, setIsReady] = signal(false);
|
|
8383
9504
|
const [isUpdateAvailable, setIsUpdateAvailable] = signal(false);
|
|
8384
9505
|
const [error, setError] = signal(null);
|
|
8385
|
-
let
|
|
9506
|
+
let unregistered = false;
|
|
9507
|
+
let unregisterRequested = false;
|
|
9508
|
+
let registrationPromise = null;
|
|
9509
|
+
let arrivedRegistration = null;
|
|
9510
|
+
let pendingUnregister = null;
|
|
8386
9511
|
let updateFoundHandler = null;
|
|
8387
9512
|
let stateChangeHandler = null;
|
|
8388
9513
|
let trackedWorker = null;
|
|
8389
9514
|
let trackedReg = null;
|
|
9515
|
+
const supported = typeof navigator !== "undefined" && !!navigator.serviceWorker;
|
|
8390
9516
|
function detachListeners() {
|
|
8391
9517
|
if (trackedReg && updateFoundHandler) {
|
|
8392
9518
|
trackedReg.removeEventListener("updatefound", updateFoundHandler);
|
|
@@ -8399,33 +9525,53 @@ function serviceWorker(scriptUrl, options) {
|
|
|
8399
9525
|
trackedWorker = null;
|
|
8400
9526
|
trackedReg = null;
|
|
8401
9527
|
}
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
8407
|
-
|
|
8408
|
-
|
|
8409
|
-
|
|
8410
|
-
const newWorker = reg.installing;
|
|
8411
|
-
if (newWorker) {
|
|
8412
|
-
if (trackedWorker && stateChangeHandler) {
|
|
8413
|
-
trackedWorker.removeEventListener("statechange", stateChangeHandler);
|
|
8414
|
-
}
|
|
8415
|
-
trackedWorker = newWorker;
|
|
8416
|
-
stateChangeHandler = () => {
|
|
8417
|
-
if (disposed) return;
|
|
8418
|
-
if (newWorker.state === "installed" && navigator.serviceWorker.controller) {
|
|
8419
|
-
setIsUpdateAvailable(true);
|
|
8420
|
-
}
|
|
8421
|
-
};
|
|
8422
|
-
newWorker.addEventListener("statechange", stateChangeHandler);
|
|
9528
|
+
function attachListeners(reg) {
|
|
9529
|
+
trackedReg = reg;
|
|
9530
|
+
updateFoundHandler = () => {
|
|
9531
|
+
if (unregistered) return;
|
|
9532
|
+
const newWorker = reg.installing;
|
|
9533
|
+
if (newWorker) {
|
|
9534
|
+
if (trackedWorker && stateChangeHandler) {
|
|
9535
|
+
trackedWorker.removeEventListener("statechange", stateChangeHandler);
|
|
8423
9536
|
}
|
|
8424
|
-
|
|
8425
|
-
|
|
8426
|
-
|
|
8427
|
-
|
|
8428
|
-
|
|
9537
|
+
trackedWorker = newWorker;
|
|
9538
|
+
stateChangeHandler = () => {
|
|
9539
|
+
if (unregistered) return;
|
|
9540
|
+
if (newWorker.state === "installed" && navigator.serviceWorker.controller) {
|
|
9541
|
+
setIsUpdateAvailable(true);
|
|
9542
|
+
}
|
|
9543
|
+
};
|
|
9544
|
+
newWorker.addEventListener("statechange", stateChangeHandler);
|
|
9545
|
+
}
|
|
9546
|
+
};
|
|
9547
|
+
reg.addEventListener("updatefound", updateFoundHandler);
|
|
9548
|
+
}
|
|
9549
|
+
function activate(reg) {
|
|
9550
|
+
setRegistration(reg);
|
|
9551
|
+
setIsReady(true);
|
|
9552
|
+
attachListeners(reg);
|
|
9553
|
+
}
|
|
9554
|
+
function recoverFailedUnregister() {
|
|
9555
|
+
unregisterRequested = false;
|
|
9556
|
+
if (!registration() && arrivedRegistration) {
|
|
9557
|
+
activate(arrivedRegistration);
|
|
9558
|
+
arrivedRegistration = null;
|
|
9559
|
+
}
|
|
9560
|
+
}
|
|
9561
|
+
function adopt(reg) {
|
|
9562
|
+
if (unregisterRequested) {
|
|
9563
|
+
arrivedRegistration = reg;
|
|
9564
|
+
return null;
|
|
9565
|
+
}
|
|
9566
|
+
activate(reg);
|
|
9567
|
+
return reg;
|
|
9568
|
+
}
|
|
9569
|
+
if (supported) {
|
|
9570
|
+
registrationPromise = navigator.serviceWorker.register(scriptUrl, options).then(adopt).catch((err) => {
|
|
9571
|
+
if (!unregistered) {
|
|
9572
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
9573
|
+
}
|
|
9574
|
+
return null;
|
|
8429
9575
|
});
|
|
8430
9576
|
}
|
|
8431
9577
|
async function update() {
|
|
@@ -8434,19 +9580,43 @@ function serviceWorker(scriptUrl, options) {
|
|
|
8434
9580
|
await reg.update();
|
|
8435
9581
|
}
|
|
8436
9582
|
}
|
|
8437
|
-
async function
|
|
8438
|
-
|
|
8439
|
-
|
|
8440
|
-
const reg = registration();
|
|
8441
|
-
if (reg) {
|
|
8442
|
-
|
|
8443
|
-
|
|
8444
|
-
setRegistration(null);
|
|
8445
|
-
setIsReady(false);
|
|
8446
|
-
}
|
|
8447
|
-
return result;
|
|
9583
|
+
async function performUnregister() {
|
|
9584
|
+
unregisterRequested = true;
|
|
9585
|
+
if (registrationPromise) await registrationPromise.catch(() => null);
|
|
9586
|
+
const reg = registration() ?? arrivedRegistration;
|
|
9587
|
+
if (!reg) {
|
|
9588
|
+
unregisterRequested = false;
|
|
9589
|
+
return false;
|
|
8448
9590
|
}
|
|
8449
|
-
|
|
9591
|
+
let result;
|
|
9592
|
+
try {
|
|
9593
|
+
result = await reg.unregister();
|
|
9594
|
+
} catch (err) {
|
|
9595
|
+
recoverFailedUnregister();
|
|
9596
|
+
throw err;
|
|
9597
|
+
}
|
|
9598
|
+
if (result) {
|
|
9599
|
+
detachListeners();
|
|
9600
|
+
setRegistration(null);
|
|
9601
|
+
setIsReady(false);
|
|
9602
|
+
setIsUpdateAvailable(false);
|
|
9603
|
+
arrivedRegistration = null;
|
|
9604
|
+
unregistered = true;
|
|
9605
|
+
} else {
|
|
9606
|
+
recoverFailedUnregister();
|
|
9607
|
+
}
|
|
9608
|
+
return result;
|
|
9609
|
+
}
|
|
9610
|
+
async function unregister() {
|
|
9611
|
+
if (!supported || unregistered) return false;
|
|
9612
|
+
if (pendingUnregister) return pendingUnregister;
|
|
9613
|
+
const request = performUnregister();
|
|
9614
|
+
pendingUnregister = request;
|
|
9615
|
+
void request.catch(() => {
|
|
9616
|
+
}).then(() => {
|
|
9617
|
+
if (pendingUnregister === request) pendingUnregister = null;
|
|
9618
|
+
});
|
|
9619
|
+
return request;
|
|
8450
9620
|
}
|
|
8451
9621
|
return { registration, isReady, isUpdateAvailable, error, update, unregister };
|
|
8452
9622
|
}
|
|
@@ -8551,17 +9721,39 @@ function createAction(actionFn) {
|
|
|
8551
9721
|
}
|
|
8552
9722
|
|
|
8553
9723
|
// src/platform/scrollRestoration.ts
|
|
9724
|
+
var DEFAULT_STATE_KEY = "__sibuScrollKey";
|
|
8554
9725
|
function scrollRestoration(options) {
|
|
8555
9726
|
const mode = options?.mode ?? "auto";
|
|
9727
|
+
const stateKey = options?.stateKey ?? DEFAULT_STATE_KEY;
|
|
8556
9728
|
const positions = /* @__PURE__ */ new Map();
|
|
8557
9729
|
let popstateHandler = null;
|
|
8558
9730
|
let currentKey = null;
|
|
8559
|
-
|
|
9731
|
+
if (options?.getKey) {
|
|
9732
|
+
try {
|
|
9733
|
+
currentKey = options.getKey();
|
|
9734
|
+
} catch {
|
|
9735
|
+
currentKey = null;
|
|
9736
|
+
}
|
|
9737
|
+
}
|
|
9738
|
+
const record = (key) => {
|
|
8560
9739
|
if (typeof window !== "undefined") {
|
|
8561
9740
|
positions.set(key, { x: window.scrollX, y: window.scrollY });
|
|
8562
9741
|
}
|
|
8563
9742
|
currentKey = key;
|
|
8564
9743
|
};
|
|
9744
|
+
const tagCurrentEntry = (key) => {
|
|
9745
|
+
if (typeof window === "undefined" || typeof history === "undefined") return;
|
|
9746
|
+
try {
|
|
9747
|
+
const existing = history.state ?? {};
|
|
9748
|
+
if (existing[stateKey] === key) return;
|
|
9749
|
+
history.replaceState({ ...existing, [stateKey]: key }, "");
|
|
9750
|
+
} catch {
|
|
9751
|
+
}
|
|
9752
|
+
};
|
|
9753
|
+
const save = (key) => {
|
|
9754
|
+
record(key);
|
|
9755
|
+
if (mode === "auto") tagCurrentEntry(key);
|
|
9756
|
+
};
|
|
8565
9757
|
const restore = (key) => {
|
|
8566
9758
|
const pos = positions.get(key);
|
|
8567
9759
|
if (pos && typeof window !== "undefined") {
|
|
@@ -8572,11 +9764,32 @@ function scrollRestoration(options) {
|
|
|
8572
9764
|
const getPosition = (key) => {
|
|
8573
9765
|
return positions.get(key);
|
|
8574
9766
|
};
|
|
9767
|
+
const onNavigation = (key) => {
|
|
9768
|
+
if (mode !== "auto") return;
|
|
9769
|
+
if (currentKey !== null && currentKey !== key) record(currentKey);
|
|
9770
|
+
currentKey = key;
|
|
9771
|
+
tagCurrentEntry(key);
|
|
9772
|
+
};
|
|
9773
|
+
let nativeModeLease = null;
|
|
8575
9774
|
if (mode === "auto" && typeof window !== "undefined") {
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
9775
|
+
nativeModeLease = acquireScrollRestorationMode("manual");
|
|
9776
|
+
if (currentKey !== null) tagCurrentEntry(currentKey);
|
|
9777
|
+
popstateHandler = (event) => {
|
|
9778
|
+
const state2 = event.state ?? null;
|
|
9779
|
+
const rawDestination = state2 ? state2[stateKey] : void 0;
|
|
9780
|
+
const destination = typeof rawDestination === "string" ? rawDestination : null;
|
|
9781
|
+
let outgoing = currentKey;
|
|
9782
|
+
if (outgoing === null && options?.getKey) {
|
|
9783
|
+
try {
|
|
9784
|
+
outgoing = options.getKey();
|
|
9785
|
+
} catch {
|
|
9786
|
+
outgoing = null;
|
|
9787
|
+
}
|
|
9788
|
+
}
|
|
9789
|
+
if (outgoing !== null && outgoing !== destination) {
|
|
9790
|
+
record(outgoing);
|
|
8579
9791
|
}
|
|
9792
|
+
if (destination !== null) restore(destination);
|
|
8580
9793
|
};
|
|
8581
9794
|
window.addEventListener("popstate", popstateHandler);
|
|
8582
9795
|
}
|
|
@@ -8585,9 +9798,11 @@ function scrollRestoration(options) {
|
|
|
8585
9798
|
window.removeEventListener("popstate", popstateHandler);
|
|
8586
9799
|
popstateHandler = null;
|
|
8587
9800
|
}
|
|
9801
|
+
nativeModeLease?.release();
|
|
9802
|
+
nativeModeLease = null;
|
|
8588
9803
|
positions.clear();
|
|
8589
9804
|
};
|
|
8590
|
-
return { save, restore, getPosition, dispose: dispose2 };
|
|
9805
|
+
return { save, restore, onNavigation, getPosition, dispose: dispose2 };
|
|
8591
9806
|
}
|
|
8592
9807
|
|
|
8593
9808
|
// src/platform/routeMiddleware.ts
|
|
@@ -9762,8 +10977,8 @@ function initDevTools(config) {
|
|
|
9762
10977
|
function clearEvents() {
|
|
9763
10978
|
eventLog.length = 0;
|
|
9764
10979
|
}
|
|
9765
|
-
function registerComponent(name, element,
|
|
9766
|
-
hook.components.set(name, { element, state });
|
|
10980
|
+
function registerComponent(name, element, state2) {
|
|
10981
|
+
hook.components.set(name, { element, state: state2 });
|
|
9767
10982
|
emit();
|
|
9768
10983
|
}
|
|
9769
10984
|
function unregisterComponent(name) {
|
|
@@ -12114,9 +13329,9 @@ function zustandAdapter(options) {
|
|
|
12114
13329
|
return createPlugin("sibu-zustand", (ctx) => {
|
|
12115
13330
|
const { store } = options;
|
|
12116
13331
|
const [getState, setSibuState] = signal(store.getState());
|
|
12117
|
-
const unsubscribe = store.subscribe((
|
|
13332
|
+
const unsubscribe = store.subscribe((state2) => {
|
|
12118
13333
|
batch(() => {
|
|
12119
|
-
setSibuState(
|
|
13334
|
+
setSibuState(state2);
|
|
12120
13335
|
});
|
|
12121
13336
|
});
|
|
12122
13337
|
function select3(selector) {
|
|
@@ -12548,6 +13763,7 @@ var materialAdapter = componentAdapter(materialConfig);
|
|
|
12548
13763
|
lazyModule,
|
|
12549
13764
|
loadRemoteModule,
|
|
12550
13765
|
loadWasmModule,
|
|
13766
|
+
loadWasmModuleWithOptions,
|
|
12551
13767
|
loaderData,
|
|
12552
13768
|
machine,
|
|
12553
13769
|
matchesPattern,
|