sibujs 4.4.0 → 4.6.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.
Files changed (112) hide show
  1. package/README.md +11 -0
  2. package/dist/browser.cjs +339 -130
  3. package/dist/browser.d.cts +61 -16
  4. package/dist/browser.d.ts +61 -16
  5. package/dist/browser.js +8 -6
  6. package/dist/build.cjs +273 -82
  7. package/dist/build.js +15 -16
  8. package/dist/cdn.dev.global.js +12 -12
  9. package/dist/cdn.full.dev.global.js +11 -11
  10. package/dist/cdn.full.global.js +10 -10
  11. package/dist/cdn.global.js +12 -12
  12. package/dist/{chunk-IXKSNWV5.js → chunk-2INLLLMZ.js} +1 -1
  13. package/dist/chunk-3ISI6ACU.js +44 -0
  14. package/dist/{chunk-VCTAEPSB.js → chunk-47M47FOM.js} +11 -3
  15. package/dist/chunk-5HZXGZ6T.js +24 -0
  16. package/dist/{chunk-KKLW7YWL.js → chunk-5MT6SJ3P.js} +275 -144
  17. package/dist/{chunk-3BTTCZ5J.js → chunk-5Q4R7HCL.js} +42 -4
  18. package/dist/{chunk-ADM46X22.js → chunk-7GQCWFOE.js} +157 -61
  19. package/dist/{chunk-4Z3SPQ67.js → chunk-7NCARGJW.js} +16 -8
  20. package/dist/{chunk-5DXA2J44.js → chunk-7XHATCIH.js} +5 -2
  21. package/dist/{chunk-LQFGQNMV.js → chunk-AIF3Z2T7.js} +302 -39
  22. package/dist/{chunk-UCALUKB5.js → chunk-BAAG6ZTI.js} +98 -29
  23. package/dist/{chunk-NVNJH22U.js → chunk-D33YTSX5.js} +2 -2
  24. package/dist/{chunk-M2F7TZHH.js → chunk-DKGBBKOF.js} +136 -80
  25. package/dist/{chunk-NIOYEGBQ.js → chunk-HURREPU2.js} +27 -13
  26. package/dist/{chunk-HFCOH2GN.js → chunk-HYXDKS4N.js} +153 -110
  27. package/dist/chunk-J6FW5TV6.js +233 -0
  28. package/dist/{chunk-36S2YPP4.js → chunk-JSXPZCET.js} +1 -1
  29. package/dist/chunk-NYNYSPK7.js +318 -0
  30. package/dist/{chunk-HQSEH5F6.js → chunk-ORMZXBKQ.js} +108 -45
  31. package/dist/{chunk-LYVUX7NT.js → chunk-OXUY2A6L.js} +287 -130
  32. package/dist/{chunk-6GSIXTWX.js → chunk-PK6FK2G2.js} +187 -66
  33. package/dist/{chunk-W2EQ7X2L.js → chunk-QE4TTDU3.js} +47 -9
  34. package/dist/{chunk-XLO7SLIX.js → chunk-SXXVZMKZ.js} +262 -70
  35. package/dist/{chunk-R25EFXXC.js → chunk-TUCPL2HB.js} +3 -3
  36. package/dist/{chunk-RR7M3FHM.js → chunk-UZQ6ALFS.js} +3 -3
  37. package/dist/{chunk-PNIRUQ4C.js → chunk-VVWPJ543.js} +6 -8
  38. package/dist/{chunk-7GIHSAWB.js → chunk-XC4MEKGA.js} +3 -3
  39. package/dist/{chunk-SKPTERHP.js → chunk-XRRZKZYX.js} +55 -31
  40. package/dist/{contracts-DBdg9J_a.d.cts → contracts-DRIuclVT.d.cts} +10 -2
  41. package/dist/{contracts-DBdg9J_a.d.ts → contracts-DRIuclVT.d.ts} +10 -2
  42. package/dist/{customElement-CNZxEB9G.d.ts → customElement-MmInOW1U.d.cts} +59 -10
  43. package/dist/{customElement-CNZxEB9G.d.cts → customElement-MmInOW1U.d.ts} +59 -10
  44. package/dist/data.cjs +293 -125
  45. package/dist/data.d.cts +47 -9
  46. package/dist/data.d.ts +47 -9
  47. package/dist/data.js +12 -9
  48. package/dist/devtools.cjs +121 -58
  49. package/dist/devtools.js +7 -8
  50. package/dist/dispose-GEIG2KOF.js +28 -0
  51. package/dist/ecosystem.cjs +391 -112
  52. package/dist/ecosystem.d.cts +31 -7
  53. package/dist/ecosystem.d.ts +31 -7
  54. package/dist/ecosystem.js +12 -12
  55. package/dist/extras.cjs +2250 -879
  56. package/dist/extras.d.cts +10 -9
  57. package/dist/extras.d.ts +10 -9
  58. package/dist/extras.js +39 -28
  59. package/dist/index.cjs +273 -82
  60. package/dist/index.d.cts +44 -154
  61. package/dist/index.d.ts +44 -154
  62. package/dist/index.js +24 -27
  63. package/dist/motion.cjs +137 -42
  64. package/dist/motion.js +5 -5
  65. package/dist/patterns.cjs +397 -48
  66. package/dist/patterns.d.cts +32 -9
  67. package/dist/patterns.d.ts +32 -9
  68. package/dist/patterns.js +8 -8
  69. package/dist/performance.cjs +338 -217
  70. package/dist/performance.d.cts +2 -2
  71. package/dist/performance.d.ts +2 -2
  72. package/dist/performance.js +8 -9
  73. package/dist/plugin-DVgSnTfK.d.cts +112 -0
  74. package/dist/plugin-DVgSnTfK.d.ts +112 -0
  75. package/dist/plugins.cjs +587 -203
  76. package/dist/plugins.d.cts +3 -3
  77. package/dist/plugins.d.ts +3 -3
  78. package/dist/plugins.js +35 -23
  79. package/dist/signal-EotCj4hS.d.cts +110 -0
  80. package/dist/signal-EotCj4hS.d.ts +110 -0
  81. package/dist/{ssr-BiPRdZ6n.d.cts → ssr-Bli9XRW5.d.cts} +5 -0
  82. package/dist/{ssr-BiPRdZ6n.d.ts → ssr-Bli9XRW5.d.ts} +5 -0
  83. package/dist/{ssr-Y7XOEPEN.js → ssr-XOTUASDO.js} +4 -5
  84. package/dist/ssr.cjs +214 -73
  85. package/dist/ssr.d.cts +9 -3
  86. package/dist/ssr.d.ts +9 -3
  87. package/dist/ssr.js +11 -12
  88. package/dist/{startup-BMpaiMhP.d.ts → startup-BLfSeL15.d.cts} +73 -22
  89. package/dist/{startup-BMpaiMhP.d.cts → startup-BLfSeL15.d.ts} +73 -22
  90. package/dist/tagFactory-DFstCLQV.d.cts +117 -0
  91. package/dist/tagFactory-DkaNVUNV.d.ts +117 -0
  92. package/dist/testing.cjs +2481 -2153
  93. package/dist/testing.d.cts +56 -5
  94. package/dist/testing.d.ts +56 -5
  95. package/dist/testing.js +580 -307
  96. package/dist/ui.cjs +798 -328
  97. package/dist/ui.d.cts +40 -7
  98. package/dist/ui.d.ts +40 -7
  99. package/dist/ui.js +151 -56
  100. package/dist/widgets.cjs +319 -285
  101. package/dist/widgets.js +9 -10
  102. package/package.json +2 -2
  103. package/dist/chunk-2WLZ6757.js +0 -149
  104. package/dist/chunk-7NBDXVHS.js +0 -60
  105. package/dist/chunk-CCSJMTRN.js +0 -15
  106. package/dist/chunk-QKRPLZ2V.js +0 -108
  107. package/dist/chunk-WWV3SJ3L.js +0 -131
  108. package/dist/dispose-46BOMMQJ.js +0 -19
  109. package/dist/plugin-D30wlGW5.d.cts +0 -71
  110. package/dist/plugin-D30wlGW5.d.ts +0 -71
  111. package/dist/tagFactory-Bzupt4Pj.d.cts +0 -55
  112. package/dist/tagFactory-Bzupt4Pj.d.ts +0 -55
package/dist/data.cjs CHANGED
@@ -34,12 +34,14 @@ __export(data_exports, {
34
34
  preloadRoute: () => preloadRoute,
35
35
  previous: () => previous,
36
36
  query: () => query,
37
+ renderWithLoader: () => renderWithLoader,
37
38
  resource: () => resource,
38
39
  setQueryData: () => setQueryData,
39
40
  socket: () => socket,
40
41
  stream: () => stream,
41
42
  syncAdapter: () => syncAdapter,
42
43
  throttle: () => throttle,
44
+ withLoader: () => withLoader,
43
45
  withRetry: () => withRetry
44
46
  });
45
47
  module.exports = __toCommonJS(data_exports);
@@ -83,8 +85,8 @@ function safeStringify(value) {
83
85
  return Object.prototype.toString.call(value);
84
86
  }
85
87
  }
86
- function describe(context2) {
87
- return context2.name ? `${context2.phase} "${context2.name}"` : context2.phase;
88
+ function describe(context) {
89
+ return context.name ? `${context.phase} "${context.name}"` : context.phase;
88
90
  }
89
91
  var REACTIVE_REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
90
92
  function untrackedRun(fn) {
@@ -92,8 +94,8 @@ function untrackedRun(fn) {
92
94
  return api?.untracked ? api.untracked(fn) : fn();
93
95
  }
94
96
  var PROPAGATE_EVENT = "sibu:error-propagate";
95
- function claimedByBoundary(error, context2) {
96
- const raw = context2.node;
97
+ function claimedByBoundary(error, context) {
98
+ const raw = context.node;
97
99
  if (!raw || typeof CustomEvent === "undefined") return false;
98
100
  const target = resolveEventTarget(raw);
99
101
  if (!target) return false;
@@ -101,7 +103,7 @@ function claimedByBoundary(error, context2) {
101
103
  const event = new CustomEvent(PROPAGATE_EVENT, {
102
104
  bubbles: true,
103
105
  cancelable: true,
104
- detail: { error, context: context2 }
106
+ detail: { error, context }
105
107
  });
106
108
  return target.dispatchEvent(event) === false;
107
109
  } catch {
@@ -120,20 +122,20 @@ function resolveEventTarget(node) {
120
122
  const self = node;
121
123
  return typeof self.dispatchEvent === "function" ? self : null;
122
124
  }
123
- function defaultReport(error, context2) {
125
+ function defaultReport(error, context) {
124
126
  if (typeof console === "undefined") return;
125
- console.error(`[SibuJS] Uncaught error during ${describe(context2)}: ${error.message}`, error);
127
+ console.error(`[SibuJS] Uncaught error during ${describe(context)}: ${error.message}`, error);
126
128
  }
127
- function reportError(error, context2) {
129
+ function reportError(error, context) {
128
130
  const err = normalizeError(error);
129
131
  untrackedRun(() => {
130
- if (claimedByBoundary(err, context2)) return;
132
+ if (claimedByBoundary(err, context)) return;
131
133
  const s = state();
132
134
  const handler = s.handler;
133
135
  if (handler && s.handlerDepth === 0) {
134
136
  s.handlerDepth++;
135
137
  try {
136
- handler(error instanceof Error ? error : err, context2);
138
+ handler(error instanceof Error ? error : err, context);
137
139
  return;
138
140
  } catch (handlerError) {
139
141
  if (typeof console !== "undefined") {
@@ -144,10 +146,29 @@ function reportError(error, context2) {
144
146
  s.handlerDepth--;
145
147
  }
146
148
  }
147
- defaultReport(err, context2);
149
+ defaultReport(err, context);
148
150
  });
149
151
  }
150
152
 
153
+ // src/core/rendering/dispose.ts
154
+ function resolveCapture(capture) {
155
+ let frame = capture;
156
+ while (frame?.closed) frame = frame.forwardTo;
157
+ return frame ?? null;
158
+ }
159
+ var registrationCaptures = [];
160
+ function currentDisposerCapture() {
161
+ return resolveCapture(registrationCaptures[registrationCaptures.length - 1]);
162
+ }
163
+ function beginDisposerCapture(capture) {
164
+ if (capture === null && registrationCaptures.length === 0) return false;
165
+ registrationCaptures.push(resolveCapture(capture));
166
+ return true;
167
+ }
168
+ function endDisposerCapture() {
169
+ registrationCaptures.pop();
170
+ }
171
+
151
172
  // src/reactivity/track-core.ts
152
173
  var POOL_MAX = 4096;
153
174
  var nodePool = [];
@@ -295,8 +316,18 @@ function untracked(fn) {
295
316
  var subscriberEpochCounter = 0;
296
317
  function retrack(effectFn, subscriber) {
297
318
  const prev = currentSubscriber;
319
+ let savedDepth = 0;
320
+ let savedSuspendSub = null;
321
+ if (trackingSuspended) {
322
+ savedDepth = suspendDepth;
323
+ savedSuspendSub = suspendSavedSub;
324
+ suspendDepth = 0;
325
+ suspendSavedSub = null;
326
+ trackingSuspended = false;
327
+ }
298
328
  currentSubscriber = subscriber;
299
329
  const sub = subscriber;
330
+ if (sub._cap === void 0) sub._cap = currentDisposerCapture();
300
331
  const epoch = ++subscriberEpochCounter;
301
332
  sub._epoch = epoch;
302
333
  sub._structDirty = false;
@@ -309,6 +340,11 @@ function retrack(effectFn, subscriber) {
309
340
  effectFn();
310
341
  } finally {
311
342
  currentSubscriber = prev;
343
+ if (savedDepth !== 0) {
344
+ suspendDepth = savedDepth;
345
+ suspendSavedSub = savedSuspendSub;
346
+ trackingSuspended = true;
347
+ }
312
348
  let node = sub.depsHead ?? null;
313
349
  while (node !== null) {
314
350
  const next = node.subNext;
@@ -328,11 +364,25 @@ function track(effectFn, subscriber) {
328
364
  if (!subscriber) return reactiveBinding(effectFn);
329
365
  cleanup(subscriber);
330
366
  const prev = currentSubscriber;
367
+ let savedDepth = 0;
368
+ let savedSuspendSub = null;
369
+ if (trackingSuspended) {
370
+ savedDepth = suspendDepth;
371
+ savedSuspendSub = suspendSavedSub;
372
+ suspendDepth = 0;
373
+ suspendSavedSub = null;
374
+ trackingSuspended = false;
375
+ }
331
376
  currentSubscriber = subscriber;
332
377
  try {
333
378
  effectFn();
334
379
  } finally {
335
380
  currentSubscriber = prev;
381
+ if (savedDepth !== 0) {
382
+ suspendDepth = savedDepth;
383
+ suspendSavedSub = savedSuspendSub;
384
+ trackingSuspended = true;
385
+ }
336
386
  const sub2 = subscriber;
337
387
  for (let n = sub2.depsHead ?? null; n !== null; n = n.subNext) {
338
388
  const sig = n.sig;
@@ -469,7 +519,12 @@ function drainQueue() {
469
519
  quarantined.add(sub);
470
520
  continue;
471
521
  }
472
- safeInvoke(sub);
522
+ const pushed = beginDisposerCapture(sub._cap ?? null);
523
+ try {
524
+ safeInvoke(sub);
525
+ } finally {
526
+ if (pushed) endDisposerCapture();
527
+ }
473
528
  }
474
529
  }
475
530
  function drainNotificationQueue() {
@@ -503,8 +558,9 @@ function propagateDirty(sub) {
503
558
  if (s._c) {
504
559
  const nSig = s._sig;
505
560
  if (nSig) {
506
- if (!nSig._d) {
561
+ if (!nSig._d || nSig._f === true) {
507
562
  nSig._d = true;
563
+ nSig._f = false;
508
564
  stack.push(nSig);
509
565
  }
510
566
  } else {
@@ -604,7 +660,7 @@ function forEachSubscriber(signal2, visit) {
604
660
  }
605
661
 
606
662
  // src/reactivity/track.ts
607
- var _runtimeVersion = true ? "4.4.0" : "dev";
663
+ var _runtimeVersion = true ? "4.6.0" : "dev";
608
664
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
609
665
  function resolveReactiveApi() {
610
666
  const g = globalThis;
@@ -668,10 +724,14 @@ function derived(getter, options) {
668
724
  const cs = {};
669
725
  cs._d = false;
670
726
  cs._init = false;
727
+ cs._f = false;
671
728
  cs._g = getter;
672
729
  cs.__v = 0;
730
+ let evaluating = false;
731
+ let disposed = false;
732
+ let pendingError;
673
733
  const markDirty = () => {
674
- if (cs._d) return;
734
+ if (cs._d || disposed) return;
675
735
  cs._d = true;
676
736
  };
677
737
  markDirty._c = 1;
@@ -694,33 +754,53 @@ function derived(getter, options) {
694
754
  }
695
755
  }, markDirty);
696
756
  const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
697
- let evaluating = false;
698
757
  const validate = () => {
699
- if (!cs._d) return;
758
+ if (!cs._d || disposed || pendingError !== void 0) return;
700
759
  const oldValue = cs._v;
701
760
  evaluating = true;
702
761
  try {
703
762
  retrack2(recompute, markDirty);
704
763
  if (!Object.is(oldValue, cs._v)) cs.__v++;
764
+ } catch (err) {
765
+ pendingError = { error: err };
766
+ cs.__v++;
767
+ if (disposed) cs._d = true;
768
+ else cs._f = true;
705
769
  } finally {
706
770
  evaluating = false;
771
+ if (disposed) cleanup2(markDirty);
707
772
  }
708
- if (hook && !Object.is(oldValue, cs._v)) {
773
+ if (hook && !disposed && !Object.is(oldValue, cs._v)) {
709
774
  hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
710
775
  }
711
776
  };
712
777
  cs._validate = validate;
778
+ const throwPending = () => {
779
+ const { error } = pendingError;
780
+ pendingError = void 0;
781
+ if (disposed) cs._d = false;
782
+ throw error;
783
+ };
713
784
  function computedGetter() {
714
785
  if (evaluating) {
715
786
  throw new Error(
716
787
  `[SibuJS] Circular dependency detected in derived${debugName ? ` "${debugName}"` : ""}. A derived signal cannot read itself (directly or through a chain).`
717
788
  );
718
789
  }
719
- if (isTrackingSuspended2()) {
720
- if (cs._d) validate();
790
+ if (isTrackingSuspended2() || disposed) {
791
+ if (cs._d) {
792
+ validate();
793
+ if (pendingError !== void 0) throwPending();
794
+ }
721
795
  return cs._v;
722
796
  }
723
- if (cs._d) validate();
797
+ if (cs._d) {
798
+ validate();
799
+ if (pendingError !== void 0) {
800
+ recordDependency2(cs);
801
+ throwPending();
802
+ }
803
+ }
724
804
  recordDependency2(cs);
725
805
  return cs._v;
726
806
  }
@@ -729,6 +809,19 @@ function derived(getter, options) {
729
809
  cs.__name = debugName;
730
810
  }
731
811
  computedGetter.__signal = cs;
812
+ computedGetter.dispose = () => {
813
+ if (disposed) return;
814
+ disposed = true;
815
+ cs._d = pendingError !== void 0;
816
+ cleanup2(markDirty);
817
+ const h = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
818
+ if (h) {
819
+ try {
820
+ h.emit("computed:destroy", { signal: cs, getter: computedGetter });
821
+ } catch {
822
+ }
823
+ }
824
+ };
732
825
  if (hook) hook.emit("computed:create", { signal: cs, name: debugName, getter: computedGetter });
733
826
  return computedGetter;
734
827
  }
@@ -1402,6 +1495,8 @@ function query(key, fetcher, options = {}) {
1402
1495
  if (disposed) return;
1403
1496
  disposed = true;
1404
1497
  effectCleanup();
1498
+ loading.dispose();
1499
+ isStale.dispose();
1405
1500
  if (intervalTimer) clearInterval(intervalTimer);
1406
1501
  detachFromEntry();
1407
1502
  if (focusHandler && typeof globalThis.removeEventListener === "function") {
@@ -1487,7 +1582,7 @@ function mutation(mutationFn, options = {}) {
1487
1582
  abortController = new AbortController();
1488
1583
  const signal2 = abortController.signal;
1489
1584
  const myRun = ++runId;
1490
- let context2;
1585
+ let context;
1491
1586
  batch(() => {
1492
1587
  setLoading(true);
1493
1588
  setError(void 0);
@@ -1495,7 +1590,7 @@ function mutation(mutationFn, options = {}) {
1495
1590
  });
1496
1591
  try {
1497
1592
  if (options.onMutate) {
1498
- context2 = await options.onMutate(variables);
1593
+ context = await options.onMutate(variables);
1499
1594
  }
1500
1595
  const result = await withRetry(() => mutationFn(variables, signal2), options.retry, void 0, signal2);
1501
1596
  if (myRun !== runId) return result;
@@ -1506,8 +1601,8 @@ function mutation(mutationFn, options = {}) {
1506
1601
  setLoading(false);
1507
1602
  setStatus("success");
1508
1603
  });
1509
- runCallback("mutation onSuccess", () => options.onSuccess?.(result, variables, context2));
1510
- runCallback("mutation onSettled", () => options.onSettled?.(result, void 0, variables, context2));
1604
+ runCallback("mutation onSuccess", () => options.onSuccess?.(result, variables, context));
1605
+ runCallback("mutation onSettled", () => options.onSettled?.(result, void 0, variables, context));
1511
1606
  return result;
1512
1607
  } catch (err) {
1513
1608
  if (isAbortError(err)) {
@@ -1529,8 +1624,8 @@ function mutation(mutationFn, options = {}) {
1529
1624
  setLoading(false);
1530
1625
  setStatus("error");
1531
1626
  });
1532
- runCallback("mutation onError", () => options.onError?.(errorObj, variables, context2));
1533
- runCallback("mutation onSettled", () => options.onSettled?.(void 0, errorObj, variables, context2));
1627
+ runCallback("mutation onError", () => options.onError?.(errorObj, variables, context));
1628
+ runCallback("mutation onSettled", () => options.onSettled?.(void 0, errorObj, variables, context));
1534
1629
  throw errorObj;
1535
1630
  }
1536
1631
  }
@@ -1668,18 +1763,21 @@ function infiniteQuery(key, fetcher, options) {
1668
1763
  }
1669
1764
  });
1670
1765
  function fetchNextPage() {
1766
+ if (disposed) return Promise.resolve();
1671
1767
  if (inFlight) return inFlight;
1672
1768
  const param = nextPageParam();
1673
1769
  if (param === void 0) return Promise.resolve();
1674
1770
  return fetchPage(param, "next");
1675
1771
  }
1676
1772
  function fetchPreviousPage() {
1773
+ if (disposed) return Promise.resolve();
1677
1774
  if (inFlight) return inFlight;
1678
1775
  const param = prevPageParam();
1679
1776
  if (param === void 0) return Promise.resolve();
1680
1777
  return fetchPage(param, "prev");
1681
1778
  }
1682
1779
  async function refetch() {
1780
+ if (disposed) return;
1683
1781
  batch(() => {
1684
1782
  setPages([]);
1685
1783
  setNextPageParam(initialPageParam);
@@ -1691,6 +1789,10 @@ function infiniteQuery(key, fetcher, options) {
1691
1789
  disposed = true;
1692
1790
  abortController?.abort();
1693
1791
  effectCleanup();
1792
+ data.dispose();
1793
+ loading.dispose();
1794
+ hasNextPage.dispose();
1795
+ hasPreviousPage.dispose();
1694
1796
  }
1695
1797
  return {
1696
1798
  data,
@@ -1759,25 +1861,26 @@ function throttle(getter, interval) {
1759
1861
  let pending = null;
1760
1862
  let lastEmitted = getter();
1761
1863
  let timer = null;
1864
+ function emit(value) {
1865
+ setThrottled(value);
1866
+ lastEmitted = value;
1867
+ cooldown = true;
1868
+ pending = null;
1869
+ timer = setTimeout(endWindow, interval);
1870
+ }
1871
+ function endWindow() {
1872
+ timer = null;
1873
+ if (pending !== null && !Object.is(pending.value, lastEmitted)) {
1874
+ emit(pending.value);
1875
+ return;
1876
+ }
1877
+ pending = null;
1878
+ cooldown = false;
1879
+ }
1762
1880
  const stop = effect(() => {
1763
1881
  const value = getter();
1764
1882
  if (!cooldown) {
1765
- if (!Object.is(value, lastEmitted)) {
1766
- setThrottled(value);
1767
- lastEmitted = value;
1768
- cooldown = true;
1769
- pending = null;
1770
- timer = setTimeout(() => {
1771
- timer = null;
1772
- cooldown = false;
1773
- if (pending !== null) {
1774
- const trailingValue = pending.value;
1775
- pending = null;
1776
- setThrottled(trailingValue);
1777
- lastEmitted = trailingValue;
1778
- }
1779
- }, interval);
1780
- }
1883
+ if (!Object.is(value, lastEmitted)) emit(value);
1781
1884
  } else {
1782
1885
  pending = { value };
1783
1886
  }
@@ -1851,7 +1954,7 @@ function resource(sourceOrFetcher, fetcherOrOptions, maybeOptions) {
1851
1954
  });
1852
1955
  runCallback("resource onError", () => options.onError?.(errorObj));
1853
1956
  } finally {
1854
- if (version === fetchVersion) {
1957
+ if (!disposed && version === fetchVersion) {
1855
1958
  runCallback("resource onSettled", () => options.onSettled?.());
1856
1959
  }
1857
1960
  }
@@ -2157,62 +2260,55 @@ function syncAdapter(config) {
2157
2260
  return config;
2158
2261
  }
2159
2262
 
2160
- // src/core/rendering/context.ts
2161
- function context(defaultValue) {
2162
- const [getValue, setValue] = signal(defaultValue);
2163
- const warnIfSSR = (method) => {
2164
- if (!DEV || !isSSR()) return;
2165
- devWarn(
2166
- `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.`
2167
- );
2168
- };
2169
- const ctx = {
2170
- provide(value) {
2171
- warnIfSSR("provide");
2172
- const previous2 = getValue();
2173
- setValue(value);
2174
- return () => setValue(previous2);
2175
- },
2176
- use() {
2177
- return getValue;
2178
- },
2179
- get() {
2180
- return getValue();
2181
- },
2182
- set(value) {
2183
- warnIfSSR("set");
2184
- setValue(value);
2185
- },
2186
- withContext(value, fn) {
2187
- warnIfSSR("withContext");
2188
- const previous2 = getValue();
2189
- setValue(value);
2190
- try {
2191
- const result = fn();
2192
- if (DEV && result !== null && typeof result?.then === "function") {
2193
- devWarn(
2194
- "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."
2195
- );
2196
- }
2197
- return result;
2198
- } finally {
2199
- setValue(previous2);
2200
- }
2201
- }
2202
- };
2203
- return ctx;
2204
- }
2205
-
2206
2263
  // src/data/routeLoader.ts
2207
- var LoaderContext = context(null);
2208
- function executeLoader(loader, context2, options) {
2209
- const res = resource(({ signal: signal2 }) => loader(context2, { signal: signal2 }), options);
2210
- LoaderContext.provide(res);
2264
+ var _clientScope = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.routeLoader.v2"), () => ({ stack: [] }));
2265
+ var _disposed = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.routeLoader.disposed.v1"), () => /* @__PURE__ */ new WeakSet());
2266
+ function currentScope() {
2267
+ const cache = getRequestScopedCache("routeLoader");
2268
+ if (!cache) return _clientScope;
2269
+ let scope = cache.get("scope");
2270
+ if (!scope) {
2271
+ scope = { stack: [] };
2272
+ cache.set("scope", scope);
2273
+ }
2274
+ return scope;
2275
+ }
2276
+ function executeLoader(loader, context, options) {
2277
+ const res = resource(({ signal: signal2 }) => loader(context, { signal: signal2 }), options);
2278
+ const disposeResource = res.dispose;
2279
+ res.dispose = () => {
2280
+ _disposed.add(res);
2281
+ disposeResource();
2282
+ };
2211
2283
  return res;
2212
2284
  }
2285
+ function renderWithLoader(loader, render) {
2286
+ if (_disposed.has(loader)) {
2287
+ throw new Error("renderWithLoader: the loader resource has been disposed");
2288
+ }
2289
+ const scope = currentScope();
2290
+ const entry = loader;
2291
+ scope.stack.push(entry);
2292
+ try {
2293
+ return render();
2294
+ } finally {
2295
+ const index = scope.stack.lastIndexOf(entry);
2296
+ if (index !== -1) scope.stack.splice(index, 1);
2297
+ }
2298
+ }
2299
+ function withLoader(loader, context, render, options) {
2300
+ const res = executeLoader(loader, context, options);
2301
+ try {
2302
+ return { view: renderWithLoader(res, render), resource: res };
2303
+ } catch (err) {
2304
+ res.dispose();
2305
+ throw err;
2306
+ }
2307
+ }
2213
2308
  function loaderData() {
2214
- const resource2 = LoaderContext.get();
2215
- if (!resource2) {
2309
+ const scope = currentScope();
2310
+ const resource2 = scope.stack[scope.stack.length - 1];
2311
+ if (!resource2 || _disposed.has(resource2)) {
2216
2312
  throw new Error("loaderData must be used inside a route with a loader");
2217
2313
  }
2218
2314
  return {
@@ -2221,7 +2317,7 @@ function loaderData() {
2221
2317
  error: resource2.error
2222
2318
  };
2223
2319
  }
2224
- async function preloadRoute(route, context2, callerSignal) {
2320
+ async function preloadRoute(route, context, callerSignal) {
2225
2321
  if (!route.loader) return void 0;
2226
2322
  const controller = new AbortController();
2227
2323
  let onAbort = null;
@@ -2234,7 +2330,7 @@ async function preloadRoute(route, context2, callerSignal) {
2234
2330
  }
2235
2331
  }
2236
2332
  try {
2237
- return await route.loader(context2, { signal: controller.signal });
2333
+ return await route.loader(context, { signal: controller.signal });
2238
2334
  } finally {
2239
2335
  if (onAbort) callerSignal?.removeEventListener("abort", onAbort);
2240
2336
  }
@@ -2262,34 +2358,73 @@ function socket(url, options) {
2262
2358
  let heartbeatTimer = null;
2263
2359
  let disposed = false;
2264
2360
  let manuallyClosed = false;
2361
+ let lifecycle = 0;
2265
2362
  function getUrl() {
2266
2363
  return typeof url === "function" ? url() : url;
2267
2364
  }
2268
2365
  function connect() {
2269
2366
  if (disposed) return;
2367
+ const generation = lifecycle;
2368
+ const invalidated = () => disposed || generation !== lifecycle;
2270
2369
  if (typeof WebSocket === "undefined") {
2271
2370
  setStatus("closed");
2272
2371
  return;
2273
2372
  }
2274
- const safeUrl = validateWsUrl(getUrl());
2373
+ let rawUrl;
2374
+ try {
2375
+ rawUrl = getUrl();
2376
+ } catch (error) {
2377
+ setStatus("closed");
2378
+ reportError(error, { phase: "async", name: "socket" });
2379
+ return;
2380
+ }
2381
+ if (invalidated()) {
2382
+ setStatus("closed");
2383
+ return;
2384
+ }
2385
+ const safeUrl = validateWsUrl(rawUrl);
2275
2386
  if (safeUrl === null) {
2276
2387
  setStatus("closed");
2277
2388
  return;
2278
2389
  }
2279
2390
  setStatus("connecting");
2280
- ws = new WebSocket(safeUrl, protocols);
2281
- ws.onopen = () => {
2391
+ if (invalidated()) {
2392
+ setStatus("closed");
2393
+ return;
2394
+ }
2395
+ let instance;
2396
+ try {
2397
+ instance = new WebSocket(safeUrl, protocols);
2398
+ } catch (error) {
2399
+ setStatus("closed");
2400
+ reportError(error, { phase: "async", name: "socket" });
2401
+ return;
2402
+ }
2403
+ if (invalidated()) {
2404
+ instance.close();
2405
+ setStatus("closed");
2406
+ return;
2407
+ }
2408
+ ws = instance;
2409
+ instance.onopen = () => {
2410
+ if (ws !== instance) return;
2411
+ const openGeneration = lifecycle;
2282
2412
  setStatus("open");
2413
+ if (disposed || openGeneration !== lifecycle || ws !== instance) return;
2283
2414
  reconnectCount = 0;
2284
2415
  startHeartbeat();
2285
2416
  };
2286
- ws.onmessage = (event) => {
2417
+ instance.onmessage = (event) => {
2418
+ if (ws !== instance) return;
2287
2419
  setData(event.data);
2288
2420
  };
2289
- ws.onclose = () => {
2421
+ instance.onclose = () => {
2422
+ if (ws !== instance) return;
2423
+ ws = null;
2424
+ const closeGeneration = lifecycle;
2290
2425
  setStatus("closed");
2291
2426
  stopHeartbeat();
2292
- const wasManual = manuallyClosed;
2427
+ const wasManual = manuallyClosed || closeGeneration !== lifecycle;
2293
2428
  manuallyClosed = false;
2294
2429
  if (autoReconnect && !disposed && !wasManual && reconnectCount < maxReconnects) {
2295
2430
  const cap = 3e4;
@@ -2303,7 +2438,7 @@ function socket(url, options) {
2303
2438
  }, jittered);
2304
2439
  }
2305
2440
  };
2306
- ws.onerror = () => {
2441
+ instance.onerror = () => {
2307
2442
  };
2308
2443
  }
2309
2444
  function startHeartbeat() {
@@ -2327,13 +2462,23 @@ function socket(url, options) {
2327
2462
  }
2328
2463
  }
2329
2464
  function close() {
2465
+ lifecycle++;
2330
2466
  manuallyClosed = true;
2331
2467
  if (reconnectTimer !== null) {
2332
2468
  clearTimeout(reconnectTimer);
2333
2469
  reconnectTimer = null;
2334
2470
  }
2335
2471
  stopHeartbeat();
2336
- if (ws) {
2472
+ if (!ws) {
2473
+ setStatus("closed");
2474
+ return;
2475
+ }
2476
+ if (ws.readyState === WebSocket.CLOSED) {
2477
+ ws = null;
2478
+ setStatus("closed");
2479
+ return;
2480
+ }
2481
+ if (ws.readyState !== WebSocket.CLOSING) {
2337
2482
  setStatus("closing");
2338
2483
  ws.close();
2339
2484
  }
@@ -2391,6 +2536,7 @@ function stream(url, options) {
2391
2536
  let disposed = false;
2392
2537
  let reconnectTimer = null;
2393
2538
  let attempts = 0;
2539
+ let closeGeneration = 0;
2394
2540
  function connect() {
2395
2541
  if (disposed) return;
2396
2542
  if (typeof EventSource === "undefined") {
@@ -2402,44 +2548,64 @@ function stream(url, options) {
2402
2548
  setStatus("closed");
2403
2549
  return;
2404
2550
  }
2551
+ const generation = closeGeneration;
2405
2552
  setStatus("connecting");
2406
- source = new EventSource(safeUrl, {
2553
+ if (disposed || generation !== closeGeneration) return;
2554
+ const instance = new EventSource(safeUrl, {
2407
2555
  withCredentials: options?.withCredentials ?? false
2408
2556
  });
2409
- source.onopen = () => {
2557
+ if (disposed || generation !== closeGeneration) {
2558
+ instance.close();
2559
+ return;
2560
+ }
2561
+ source = instance;
2562
+ const live = () => !disposed && source === instance;
2563
+ instance.onopen = () => {
2564
+ if (!live()) return;
2410
2565
  setStatus("open");
2411
2566
  attempts = 0;
2412
2567
  };
2413
- source.onmessage = (evt) => {
2568
+ instance.onmessage = (evt) => {
2569
+ if (!live()) return;
2414
2570
  setData(evt.data);
2415
2571
  setEvent(evt.type);
2416
2572
  };
2417
- source.onerror = () => {
2418
- if (source && source.readyState === EventSource.CLOSED) {
2419
- setStatus("closed");
2420
- source = null;
2421
- if (autoReconnect && !disposed && attempts < maxReconnects) {
2422
- const delay = Math.min(maxMs, baseMs * 2 ** attempts);
2423
- const jittered = delay * (0.5 + Math.random() * 0.5);
2424
- attempts++;
2425
- reconnectTimer = setTimeout(() => {
2426
- reconnectTimer = null;
2427
- connect();
2428
- }, jittered);
2429
- }
2573
+ instance.onerror = () => {
2574
+ if (!live() || instance.readyState !== EventSource.CLOSED) return;
2575
+ detach(instance);
2576
+ source = null;
2577
+ const generation2 = closeGeneration;
2578
+ setStatus("closed");
2579
+ if (disposed || generation2 !== closeGeneration) return;
2580
+ if (autoReconnect && attempts < maxReconnects) {
2581
+ const delay = Math.min(maxMs, baseMs * 2 ** attempts);
2582
+ const jittered = delay * (0.5 + Math.random() * 0.5);
2583
+ attempts++;
2584
+ reconnectTimer = setTimeout(() => {
2585
+ reconnectTimer = null;
2586
+ connect();
2587
+ }, jittered);
2430
2588
  }
2431
2589
  };
2432
2590
  }
2591
+ function detach(instance) {
2592
+ instance.onopen = null;
2593
+ instance.onmessage = null;
2594
+ instance.onerror = null;
2595
+ }
2433
2596
  function close() {
2597
+ closeGeneration++;
2434
2598
  if (reconnectTimer !== null) {
2435
2599
  clearTimeout(reconnectTimer);
2436
2600
  reconnectTimer = null;
2437
2601
  }
2438
- if (source) {
2439
- source.close();
2440
- setStatus("closed");
2602
+ const instance = source;
2603
+ if (instance) {
2441
2604
  source = null;
2605
+ detach(instance);
2606
+ instance.close();
2442
2607
  }
2608
+ setStatus("closed");
2443
2609
  }
2444
2610
  function dispose() {
2445
2611
  disposed = true;
@@ -2464,11 +2630,13 @@ function stream(url, options) {
2464
2630
  preloadRoute,
2465
2631
  previous,
2466
2632
  query,
2633
+ renderWithLoader,
2467
2634
  resource,
2468
2635
  setQueryData,
2469
2636
  socket,
2470
2637
  stream,
2471
2638
  syncAdapter,
2472
2639
  throttle,
2640
+ withLoader,
2473
2641
  withRetry
2474
2642
  });