sibujs 1.4.0 → 1.5.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 (79) hide show
  1. package/README.md +105 -119
  2. package/dist/browser.cjs +53 -14
  3. package/dist/browser.d.cts +14 -9
  4. package/dist/browser.d.ts +14 -9
  5. package/dist/browser.js +4 -4
  6. package/dist/build.cjs +124 -42
  7. package/dist/build.d.cts +1 -1
  8. package/dist/build.d.ts +1 -1
  9. package/dist/build.js +10 -10
  10. package/dist/cdn.global.js +7 -7
  11. package/dist/chunk-5ZYQ6KDD.js +154 -0
  12. package/dist/chunk-6BMPXPUW.js +26 -0
  13. package/dist/chunk-7GRNSCFT.js +1097 -0
  14. package/dist/chunk-BGTHZHJ5.js +1016 -0
  15. package/dist/chunk-BMPL52BF.js +654 -0
  16. package/dist/chunk-GJPXRJ45.js +37 -0
  17. package/dist/chunk-JCDUJN2F.js +2779 -0
  18. package/dist/chunk-K4G4ZQNR.js +286 -0
  19. package/dist/chunk-MB6QFH3I.js +2776 -0
  20. package/dist/chunk-MYRV7VDM.js +742 -0
  21. package/dist/chunk-NZIIMDWI.js +84 -0
  22. package/dist/chunk-P3XWXJZU.js +282 -0
  23. package/dist/chunk-PDZQY43A.js +616 -0
  24. package/dist/chunk-RJ46C3CS.js +1293 -0
  25. package/dist/chunk-SFKNRVCU.js +292 -0
  26. package/dist/chunk-TDGZL5CU.js +365 -0
  27. package/dist/chunk-VAPYJN4X.js +368 -0
  28. package/dist/chunk-VQDZK23A.js +1023 -0
  29. package/dist/chunk-VQNQZCWJ.js +61 -0
  30. package/dist/chunk-XHK6BDAJ.js +76 -0
  31. package/dist/chunk-XUEEGU5O.js +409 -0
  32. package/dist/contracts-ey_Qh8ef.d.cts +239 -0
  33. package/dist/contracts-ey_Qh8ef.d.ts +239 -0
  34. package/dist/customElement-BL3Uo8dL.d.cts +318 -0
  35. package/dist/customElement-BL3Uo8dL.d.ts +318 -0
  36. package/dist/data.cjs +52 -11
  37. package/dist/data.js +6 -6
  38. package/dist/devtools.cjs +22 -24
  39. package/dist/devtools.js +26 -28
  40. package/dist/ecosystem.cjs +31 -6
  41. package/dist/ecosystem.d.cts +4 -4
  42. package/dist/ecosystem.d.ts +4 -4
  43. package/dist/ecosystem.js +7 -7
  44. package/dist/extras.cjs +304 -108
  45. package/dist/extras.d.cts +3 -3
  46. package/dist/extras.d.ts +3 -3
  47. package/dist/extras.js +19 -19
  48. package/dist/index.cjs +124 -42
  49. package/dist/index.d.cts +58 -48
  50. package/dist/index.d.ts +58 -48
  51. package/dist/index.js +10 -10
  52. package/dist/motion.cjs +13 -2
  53. package/dist/motion.d.cts +1 -1
  54. package/dist/motion.d.ts +1 -1
  55. package/dist/motion.js +3 -3
  56. package/dist/patterns.cjs +91 -24
  57. package/dist/patterns.d.cts +46 -12
  58. package/dist/patterns.d.ts +46 -12
  59. package/dist/patterns.js +5 -5
  60. package/dist/performance.cjs +97 -12
  61. package/dist/performance.d.cts +6 -1
  62. package/dist/performance.d.ts +6 -1
  63. package/dist/performance.js +5 -3
  64. package/dist/plugins.cjs +19 -13
  65. package/dist/plugins.d.cts +3 -3
  66. package/dist/plugins.d.ts +3 -3
  67. package/dist/plugins.js +16 -18
  68. package/dist/ssr.cjs +9 -0
  69. package/dist/ssr.d.cts +1 -1
  70. package/dist/ssr.d.ts +1 -1
  71. package/dist/ssr.js +7 -7
  72. package/dist/testing.js +2 -2
  73. package/dist/ui.cjs +130 -48
  74. package/dist/ui.d.cts +13 -16
  75. package/dist/ui.d.ts +13 -16
  76. package/dist/ui.js +6 -6
  77. package/dist/widgets.cjs +31 -6
  78. package/dist/widgets.js +5 -5
  79. package/package.json +1 -1
package/dist/patterns.cjs CHANGED
@@ -430,7 +430,7 @@ function persisted(key, initial, options = {}) {
430
430
  }
431
431
  const [value, setValue] = signal(restored);
432
432
  let applyingFromStorage = false;
433
- effect(() => {
433
+ const stopPersistEffect = effect(() => {
434
434
  const current = value();
435
435
  if (applyingFromStorage) return;
436
436
  try {
@@ -470,6 +470,7 @@ function persisted(key, initial, options = {}) {
470
470
  window.addEventListener("storage", storageListener);
471
471
  }
472
472
  const dispose = () => {
473
+ stopPersistEffect();
473
474
  if (storageListener && typeof window !== "undefined") {
474
475
  window.removeEventListener("storage", storageListener);
475
476
  storageListener = null;
@@ -487,27 +488,53 @@ function persisted(key, initial, options = {}) {
487
488
  // src/patterns/optimistic.ts
488
489
  function optimistic(initialValue) {
489
490
  const [value, setValue] = signal(initialValue);
490
- const [_pending, setPending] = signal(false);
491
- async function addOptimistic(optimisticValue, asyncAction) {
491
+ const [pending, setPending] = signal(false);
492
+ let inflightCount = 0;
493
+ let version = 0;
494
+ async function update(optimisticValue, asyncAction) {
495
+ const myVersion = ++version;
492
496
  const previousValue = value();
493
497
  setValue(optimisticValue);
498
+ inflightCount++;
494
499
  setPending(true);
495
500
  try {
496
501
  const result = await asyncAction();
497
- setValue(result);
502
+ if (version === myVersion) {
503
+ setValue(result);
504
+ }
498
505
  } catch {
499
- setValue(previousValue);
506
+ if (version === myVersion) {
507
+ setValue(previousValue);
508
+ }
500
509
  } finally {
501
- setPending(false);
510
+ inflightCount--;
511
+ if (inflightCount === 0) setPending(false);
502
512
  }
503
513
  }
504
- return [value, addOptimistic];
514
+ return { value, pending, update };
505
515
  }
506
516
  function optimisticList(initialValue) {
507
517
  const [items, setItems] = signal([...initialValue]);
508
- async function addOptimistic(item, asyncAction) {
518
+ const [pending, setPending] = signal(false);
519
+ let inflightCount = 0;
520
+ let version = 0;
521
+ function begin() {
522
+ const v = ++version;
523
+ inflightCount++;
524
+ setPending(true);
525
+ return v;
526
+ }
527
+ function end(myVersion, revertFn) {
528
+ if (revertFn && version === myVersion) {
529
+ revertFn();
530
+ }
531
+ inflightCount--;
532
+ if (inflightCount === 0) setPending(false);
533
+ }
534
+ async function add(item, asyncAction) {
509
535
  const prev = items();
510
536
  setItems([...prev, item]);
537
+ const myVersion = begin();
511
538
  try {
512
539
  const result = await asyncAction();
513
540
  setItems((current) => {
@@ -517,32 +544,56 @@ function optimisticList(initialValue) {
517
544
  next[idx] = result;
518
545
  return next;
519
546
  }
520
- return [...current.filter((i) => i !== item), result];
547
+ return [...current, result];
521
548
  });
549
+ end(myVersion);
522
550
  } catch {
523
- setItems(prev);
551
+ end(myVersion, () => setItems(prev));
524
552
  }
525
553
  }
526
- async function removeOptimistic(predicate, asyncAction) {
554
+ async function remove(predicate, asyncAction) {
527
555
  const prev = items();
528
556
  setItems(prev.filter((item) => !predicate(item)));
557
+ const myVersion = begin();
529
558
  try {
530
559
  await asyncAction();
560
+ end(myVersion);
531
561
  } catch {
532
- setItems(prev);
562
+ end(myVersion, () => setItems(prev));
533
563
  }
534
564
  }
535
- async function updateOptimistic(predicate, update, asyncAction) {
565
+ async function updateItem(predicate, patch, asyncAction) {
536
566
  const prev = items();
537
- setItems(prev.map((item) => predicate(item) ? { ...item, ...update } : item));
567
+ const patchedRefs = [];
568
+ setItems(
569
+ prev.map((item) => {
570
+ if (predicate(item)) {
571
+ const patched = { ...item, ...patch };
572
+ patchedRefs.push(patched);
573
+ return patched;
574
+ }
575
+ return item;
576
+ })
577
+ );
578
+ const myVersion = begin();
538
579
  try {
539
580
  const result = await asyncAction();
540
- setItems((current) => current.map((item) => predicate(item) ? result : item));
581
+ setItems((current) => current.map((item) => patchedRefs.includes(item) ? result : item));
582
+ end(myVersion);
541
583
  } catch {
542
- setItems(prev);
584
+ end(myVersion, () => setItems(prev));
543
585
  }
544
586
  }
545
- return { items, addOptimistic, removeOptimistic, updateOptimistic };
587
+ return {
588
+ items,
589
+ pending,
590
+ add,
591
+ remove,
592
+ update: updateItem,
593
+ addOptimistic: add,
594
+ removeOptimistic: remove,
595
+ updateOptimistic: updateItem
596
+ };
546
597
  }
547
598
 
548
599
  // src/core/signals/derived.ts
@@ -563,21 +614,37 @@ function derived(getter, options) {
563
614
  cs._v = getter();
564
615
  }, markDirty);
565
616
  const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
617
+ let evaluating = false;
566
618
  function computedGetter() {
619
+ if (evaluating) {
620
+ throw new Error(
621
+ `[SibuJS] Circular dependency detected in derived${debugName ? ` "${debugName}"` : ""}. A derived signal cannot read itself (directly or through a chain).`
622
+ );
623
+ }
567
624
  if (trackingSuspended) {
568
625
  if (cs._d) {
569
- cs._d = false;
570
- cs._v = getter();
626
+ evaluating = true;
627
+ try {
628
+ cs._d = false;
629
+ cs._v = getter();
630
+ } finally {
631
+ evaluating = false;
632
+ }
571
633
  }
572
634
  return cs._v;
573
635
  }
574
636
  recordDependency(cs);
575
637
  if (cs._d) {
576
638
  const oldValue = cs._v;
577
- track(() => {
578
- cs._d = false;
579
- cs._v = getter();
580
- }, markDirty);
639
+ evaluating = true;
640
+ try {
641
+ track(() => {
642
+ cs._d = false;
643
+ cs._v = getter();
644
+ }, markDirty);
645
+ } finally {
646
+ evaluating = false;
647
+ }
581
648
  if (hook && oldValue !== cs._v) {
582
649
  hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
583
650
  }
@@ -642,7 +709,7 @@ function timeline(initial, maxHistory = 100) {
642
709
 
643
710
  // src/patterns/globalStore.ts
644
711
  function globalStore(config) {
645
- const initialState = { ...config.state };
712
+ const initialState = JSON.parse(JSON.stringify(config.state));
646
713
  const [getState, setState] = signal({ ...initialState });
647
714
  const listeners = /* @__PURE__ */ new Set();
648
715
  const middlewares = config.middleware || [];
@@ -1,4 +1,4 @@
1
- export { C as ComponentProps, P as PropDef, a as PropSchema, R as RenderProp, V as Validator, b as assertType, c as compose, d as createGuard, e as createSlots, f as defineComponent, g as defineSlottedComponent, h as defineStrictComponent, v as validateProps, i as validators, w as withBoundary, j as withDefaults, k as withProps, l as withWrapper } from './contracts-xo5ckdRP.cjs';
1
+ export { C as ComponentProps, P as PropDef, a as PropSchema, R as RenderProp, V as Validator, b as assertType, c as compose, d as createGuard, e as createSlots, f as defineComponent, g as defineSlottedComponent, h as defineStrictComponent, v as validateProps, i as validators, w as withBoundary, j as withDefaults, k as withProps, l as withWrapper } from './contracts-ey_Qh8ef.cjs';
2
2
 
3
3
  interface MachineConfig<S extends string, E extends string, C extends Record<string, unknown> = Record<string, unknown>> {
4
4
  initial: S;
@@ -88,31 +88,65 @@ interface PersistOptions<T = unknown> {
88
88
  }
89
89
  declare function persisted<T>(key: string, initial: T, options?: PersistOptions<T>): [() => T, (next: T | ((prev: T) => T)) => void];
90
90
 
91
- interface OptimisticAction<T> {
92
- optimisticValue: T;
93
- revert: T;
94
- }
95
91
  /**
96
92
  * optimistic provides optimistic UI updates that can be reverted on failure.
97
93
  * The value updates immediately, then reverts if the async operation fails.
98
94
  *
99
- * Note: concurrent optimistic updates on the same value are inherently
100
- * ambiguous. If multiple operations are in flight, a failed revert restores
101
- * the value captured before that specific operation started.
95
+ * Returns a named object with `value`, `pending`, and `update`.
96
+ *
97
+ * Concurrent safety: each operation is tagged with a version counter.
98
+ * A failing operation only reverts if no newer operation has started since,
99
+ * preventing stale snapshots from overwriting fresher optimistic state.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * const likes = optimistic(42);
104
+ *
105
+ * button(
106
+ * { on: { click: () => likes.update(likes.value() + 1, () => api.like()) } },
107
+ * () => `${likes.value()} ${likes.pending() ? "(saving…)" : ""}`,
108
+ * );
109
+ * ```
102
110
  */
103
- declare function optimistic<T>(initialValue: T): [() => T, (optimisticValue: T, asyncAction: () => Promise<T>) => Promise<void>];
111
+ declare function optimistic<T>(initialValue: T): {
112
+ value: () => T;
113
+ pending: () => boolean;
114
+ update: (optimisticValue: T, asyncAction: () => Promise<T>) => Promise<void>;
115
+ };
104
116
  /**
105
117
  * optimisticList provides optimistic updates for array state.
106
118
  *
107
119
  * Uses functional updates (setItems(current => ...)) for the success path
108
120
  * to avoid losing changes made by concurrent operations. The failure path
109
- * reverts to a snapshot taken before the operation started.
121
+ * uses a version guard so stale reverts don't overwrite newer state.
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * const todos = optimisticList<Todo>([]);
126
+ *
127
+ * todos.add(
128
+ * { id: tempId(), text: "New" },
129
+ * async () => api.createTodo("New"),
130
+ * );
131
+ *
132
+ * div([
133
+ * () => todos.pending() ? span("Saving…") : null,
134
+ * each(() => todos.items(), (t) => div(t().text), { key: (t) => t.id }),
135
+ * ]);
136
+ * ```
110
137
  */
111
138
  declare function optimisticList<T>(initialValue: T[]): {
112
139
  items: () => T[];
140
+ pending: () => boolean;
141
+ add: (item: T, asyncAction: () => Promise<T>) => Promise<void>;
142
+ remove: (predicate: (item: T) => boolean, asyncAction: () => Promise<void>) => Promise<void>;
143
+ update: (predicate: (item: T) => boolean, patch: Partial<T>, asyncAction: () => Promise<T>) => Promise<void>;
144
+ /** @deprecated Use `add` instead */
113
145
  addOptimistic: (item: T, asyncAction: () => Promise<T>) => Promise<void>;
146
+ /** @deprecated Use `remove` instead */
114
147
  removeOptimistic: (predicate: (item: T) => boolean, asyncAction: () => Promise<void>) => Promise<void>;
115
- updateOptimistic: (predicate: (item: T) => boolean, update: Partial<T>, asyncAction: () => Promise<T>) => Promise<void>;
148
+ /** @deprecated Use `update` instead */
149
+ updateOptimistic: (predicate: (item: T) => boolean, patch: Partial<T>, asyncAction: () => Promise<T>) => Promise<void>;
116
150
  };
117
151
 
118
152
  interface TimeTravelReturn<T> {
@@ -151,4 +185,4 @@ declare function globalStore<S extends Record<string, unknown>, A extends Record
151
185
  middleware?: Middleware<S>[];
152
186
  }): GlobalStore<S, A>;
153
187
 
154
- export { type GlobalStore, type MachineConfig, type MachineReturn, type Middleware, type OptimisticAction, type PersistOptions, type Selector, type TimeTravelReturn, globalStore, machine, optimistic, optimisticList, persisted, timeline };
188
+ export { type GlobalStore, type MachineConfig, type MachineReturn, type Middleware, type PersistOptions, type Selector, type TimeTravelReturn, globalStore, machine, optimistic, optimisticList, persisted, timeline };
@@ -1,4 +1,4 @@
1
- export { C as ComponentProps, P as PropDef, a as PropSchema, R as RenderProp, V as Validator, b as assertType, c as compose, d as createGuard, e as createSlots, f as defineComponent, g as defineSlottedComponent, h as defineStrictComponent, v as validateProps, i as validators, w as withBoundary, j as withDefaults, k as withProps, l as withWrapper } from './contracts-xo5ckdRP.js';
1
+ export { C as ComponentProps, P as PropDef, a as PropSchema, R as RenderProp, V as Validator, b as assertType, c as compose, d as createGuard, e as createSlots, f as defineComponent, g as defineSlottedComponent, h as defineStrictComponent, v as validateProps, i as validators, w as withBoundary, j as withDefaults, k as withProps, l as withWrapper } from './contracts-ey_Qh8ef.js';
2
2
 
3
3
  interface MachineConfig<S extends string, E extends string, C extends Record<string, unknown> = Record<string, unknown>> {
4
4
  initial: S;
@@ -88,31 +88,65 @@ interface PersistOptions<T = unknown> {
88
88
  }
89
89
  declare function persisted<T>(key: string, initial: T, options?: PersistOptions<T>): [() => T, (next: T | ((prev: T) => T)) => void];
90
90
 
91
- interface OptimisticAction<T> {
92
- optimisticValue: T;
93
- revert: T;
94
- }
95
91
  /**
96
92
  * optimistic provides optimistic UI updates that can be reverted on failure.
97
93
  * The value updates immediately, then reverts if the async operation fails.
98
94
  *
99
- * Note: concurrent optimistic updates on the same value are inherently
100
- * ambiguous. If multiple operations are in flight, a failed revert restores
101
- * the value captured before that specific operation started.
95
+ * Returns a named object with `value`, `pending`, and `update`.
96
+ *
97
+ * Concurrent safety: each operation is tagged with a version counter.
98
+ * A failing operation only reverts if no newer operation has started since,
99
+ * preventing stale snapshots from overwriting fresher optimistic state.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * const likes = optimistic(42);
104
+ *
105
+ * button(
106
+ * { on: { click: () => likes.update(likes.value() + 1, () => api.like()) } },
107
+ * () => `${likes.value()} ${likes.pending() ? "(saving…)" : ""}`,
108
+ * );
109
+ * ```
102
110
  */
103
- declare function optimistic<T>(initialValue: T): [() => T, (optimisticValue: T, asyncAction: () => Promise<T>) => Promise<void>];
111
+ declare function optimistic<T>(initialValue: T): {
112
+ value: () => T;
113
+ pending: () => boolean;
114
+ update: (optimisticValue: T, asyncAction: () => Promise<T>) => Promise<void>;
115
+ };
104
116
  /**
105
117
  * optimisticList provides optimistic updates for array state.
106
118
  *
107
119
  * Uses functional updates (setItems(current => ...)) for the success path
108
120
  * to avoid losing changes made by concurrent operations. The failure path
109
- * reverts to a snapshot taken before the operation started.
121
+ * uses a version guard so stale reverts don't overwrite newer state.
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * const todos = optimisticList<Todo>([]);
126
+ *
127
+ * todos.add(
128
+ * { id: tempId(), text: "New" },
129
+ * async () => api.createTodo("New"),
130
+ * );
131
+ *
132
+ * div([
133
+ * () => todos.pending() ? span("Saving…") : null,
134
+ * each(() => todos.items(), (t) => div(t().text), { key: (t) => t.id }),
135
+ * ]);
136
+ * ```
110
137
  */
111
138
  declare function optimisticList<T>(initialValue: T[]): {
112
139
  items: () => T[];
140
+ pending: () => boolean;
141
+ add: (item: T, asyncAction: () => Promise<T>) => Promise<void>;
142
+ remove: (predicate: (item: T) => boolean, asyncAction: () => Promise<void>) => Promise<void>;
143
+ update: (predicate: (item: T) => boolean, patch: Partial<T>, asyncAction: () => Promise<T>) => Promise<void>;
144
+ /** @deprecated Use `add` instead */
113
145
  addOptimistic: (item: T, asyncAction: () => Promise<T>) => Promise<void>;
146
+ /** @deprecated Use `remove` instead */
114
147
  removeOptimistic: (predicate: (item: T) => boolean, asyncAction: () => Promise<void>) => Promise<void>;
115
- updateOptimistic: (predicate: (item: T) => boolean, update: Partial<T>, asyncAction: () => Promise<T>) => Promise<void>;
148
+ /** @deprecated Use `update` instead */
149
+ updateOptimistic: (predicate: (item: T) => boolean, patch: Partial<T>, asyncAction: () => Promise<T>) => Promise<void>;
116
150
  };
117
151
 
118
152
  interface TimeTravelReturn<T> {
@@ -151,4 +185,4 @@ declare function globalStore<S extends Record<string, unknown>, A extends Record
151
185
  middleware?: Middleware<S>[];
152
186
  }): GlobalStore<S, A>;
153
187
 
154
- export { type GlobalStore, type MachineConfig, type MachineReturn, type Middleware, type OptimisticAction, type PersistOptions, type Selector, type TimeTravelReturn, globalStore, machine, optimistic, optimisticList, persisted, timeline };
188
+ export { type GlobalStore, type MachineConfig, type MachineReturn, type Middleware, type PersistOptions, type Selector, type TimeTravelReturn, globalStore, machine, optimistic, optimisticList, persisted, timeline };
package/dist/patterns.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  optimisticList,
6
6
  persisted,
7
7
  timeline
8
- } from "./chunk-ZWKZCBO6.js";
8
+ } from "./chunk-VAPYJN4X.js";
9
9
  import {
10
10
  RenderProp,
11
11
  assertType,
@@ -22,11 +22,11 @@ import {
22
22
  withProps,
23
23
  withWrapper
24
24
  } from "./chunk-CNZ35WI2.js";
25
- import "./chunk-NEKUBFPT.js";
26
- import "./chunk-CHF5OHIA.js";
25
+ import "./chunk-XHK6BDAJ.js";
26
+ import "./chunk-VQNQZCWJ.js";
27
27
  import "./chunk-EUZND3CB.js";
28
- import "./chunk-WZSPOOER.js";
29
- import "./chunk-ZD6OAMTH.js";
28
+ import "./chunk-NZIIMDWI.js";
29
+ import "./chunk-K4G4ZQNR.js";
30
30
  import "./chunk-5X6PP2UK.js";
31
31
  export {
32
32
  RenderProp,
@@ -189,6 +189,11 @@ function isDev() {
189
189
  return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
190
190
  }
191
191
  var _isDev = isDev();
192
+ function devAssert(condition, message) {
193
+ if (_isDev && !condition) {
194
+ throw new Error(`[Sibu] ${message}`);
195
+ }
196
+ }
192
197
  function devWarn(message) {
193
198
  if (_isDev) {
194
199
  console.warn(`[Sibu] ${message}`);
@@ -198,6 +203,8 @@ function devWarn(message) {
198
203
  // src/reactivity/track.ts
199
204
  var _isDev2 = isDev();
200
205
  var subscriberStack = new Array(32);
206
+ var stackCapacity = 32;
207
+ var stackTop = -1;
201
208
  var currentSubscriber = null;
202
209
  var signalSubscribers = /* @__PURE__ */ new WeakMap();
203
210
  var SUBS = "__s";
@@ -211,6 +218,24 @@ function safeInvoke(sub) {
211
218
  if (_isDev2) devWarn(`Subscriber threw during notification: ${err instanceof Error ? err.message : String(err)}`);
212
219
  }
213
220
  }
221
+ function track(effectFn, subscriber) {
222
+ if (!subscriber) subscriber = effectFn;
223
+ cleanup(subscriber);
224
+ ++stackTop;
225
+ if (stackTop >= stackCapacity) {
226
+ stackCapacity *= 2;
227
+ subscriberStack.length = stackCapacity;
228
+ }
229
+ subscriberStack[stackTop] = subscriber;
230
+ currentSubscriber = subscriber;
231
+ try {
232
+ effectFn();
233
+ } finally {
234
+ stackTop--;
235
+ currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
236
+ }
237
+ return () => cleanup(subscriber);
238
+ }
214
239
  function recordDependency(signal2) {
215
240
  if (!currentSubscriber) return;
216
241
  const sub = currentSubscriber;
@@ -352,6 +377,69 @@ function notifySubscribers(signal2) {
352
377
  notifyDepth--;
353
378
  }
354
379
  }
380
+ function cleanup(subscriber) {
381
+ const sub = subscriber;
382
+ const singleDep = sub._dep;
383
+ if (singleDep !== void 0) {
384
+ const subs = singleDep[SUBS];
385
+ if (subs) {
386
+ subs.delete(subscriber);
387
+ if (singleDep.__f === subscriber) {
388
+ singleDep.__f = void 0;
389
+ }
390
+ }
391
+ sub._dep = void 0;
392
+ return;
393
+ }
394
+ const deps = sub._deps;
395
+ if (!deps || deps.size === 0) return;
396
+ for (const signal2 of deps) {
397
+ const subs = signal2[SUBS];
398
+ if (subs) {
399
+ subs.delete(subscriber);
400
+ if (signal2.__f === subscriber) {
401
+ signal2.__f = void 0;
402
+ }
403
+ }
404
+ }
405
+ deps.clear();
406
+ }
407
+
408
+ // src/core/ssr-context.ts
409
+ var ssrMode = false;
410
+ function isSSR() {
411
+ return ssrMode;
412
+ }
413
+
414
+ // src/core/signals/effect.ts
415
+ var _g = globalThis;
416
+ function effect(effectFn, options) {
417
+ devAssert(typeof effectFn === "function", "effect: argument must be a function.");
418
+ if (isSSR()) return () => {
419
+ };
420
+ const onError = options?.onError;
421
+ const wrappedFn = onError ? () => {
422
+ try {
423
+ effectFn();
424
+ } catch (err) {
425
+ onError(err);
426
+ }
427
+ } : effectFn;
428
+ let cleanupHandle = () => {
429
+ };
430
+ const subscriber = () => {
431
+ cleanupHandle();
432
+ cleanupHandle = track(wrappedFn, subscriber);
433
+ };
434
+ cleanupHandle = track(wrappedFn, subscriber);
435
+ const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
436
+ if (hook) hook.emit("effect:create", { effectFn });
437
+ return () => {
438
+ const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
439
+ if (h) h.emit("effect:destroy", { effectFn });
440
+ cleanupHandle();
441
+ };
442
+ }
355
443
 
356
444
  // src/reactivity/batch.ts
357
445
  var batchDepth = 0;
@@ -363,7 +451,7 @@ function enqueueBatchedSignal(signal2) {
363
451
  }
364
452
 
365
453
  // src/core/signals/signal.ts
366
- var _g = globalThis;
454
+ var _g2 = globalThis;
367
455
  var _isDev3 = isDev();
368
456
  function signal(initial, options) {
369
457
  const state = { value: initial };
@@ -384,7 +472,7 @@ function signal(initial, options) {
384
472
  if (_isDev3) {
385
473
  const oldValue = state.value;
386
474
  state.value = newValue;
387
- const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
475
+ const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
388
476
  if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue, newValue });
389
477
  } else {
390
478
  state.value = newValue;
@@ -394,7 +482,7 @@ function signal(initial, options) {
394
482
  }
395
483
  }
396
484
  if (_isDev3) {
397
- const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
485
+ const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
398
486
  if (hook) hook.emit("signal:create", { signal: state, name: debugName, getter: get, initial });
399
487
  }
400
488
  return [get, set];
@@ -406,15 +494,12 @@ function startTransition(callback) {
406
494
  }
407
495
  function deferredValue(getter) {
408
496
  const [deferred, setDeferred] = signal(getter());
409
- const sync = () => {
410
- const current = getter();
411
- setDeferred(current);
412
- };
413
- scheduleUpdate(Priority.LOW, sync);
414
- return () => {
415
- scheduleUpdate(Priority.LOW, sync);
416
- return deferred();
417
- };
497
+ let latest = deferred();
498
+ effect(() => {
499
+ latest = getter();
500
+ scheduleUpdate(Priority.LOW, () => setDeferred(latest));
501
+ });
502
+ return deferred;
418
503
  }
419
504
  function transitionState() {
420
505
  const [isPending, setIsPending] = signal(false);
@@ -56,6 +56,11 @@ declare function startTransition(callback: () => void): void;
56
56
  * The deferred value mirrors the source but updates at LOW priority,
57
57
  * allowing the UI to remain responsive while expensive derived state
58
58
  * catches up.
59
+ *
60
+ * Uses an effect to subscribe to the source getter. When the source
61
+ * changes, a LOW-priority update is scheduled. The deferred signal
62
+ * only updates when the scheduler flushes, so fast bursts of source
63
+ * changes collapse into a single deferred update.
59
64
  */
60
65
  declare function deferredValue<T>(getter: () => T): () => T;
61
66
  /**
@@ -89,7 +94,7 @@ declare function setIdPrefix(prefix: string): void;
89
94
  * @example
90
95
  * ```ts
91
96
  * const id = id();
92
- * label({ htmlFor: id, nodes: "Name" });
97
+ * label({ htmlFor: id }, "Name");
93
98
  * input({ id, type: "text" });
94
99
  * ```
95
100
  */
@@ -56,6 +56,11 @@ declare function startTransition(callback: () => void): void;
56
56
  * The deferred value mirrors the source but updates at LOW priority,
57
57
  * allowing the UI to remain responsive while expensive derived state
58
58
  * catches up.
59
+ *
60
+ * Uses an effect to subscribe to the source getter. When the source
61
+ * changes, a LOW-priority update is scheduled. The deferred signal
62
+ * only updates when the scheduler flushes, so fast bursts of source
63
+ * changes collapse into a single deferred update.
59
64
  */
60
65
  declare function deferredValue<T>(getter: () => T): () => T;
61
66
  /**
@@ -89,7 +94,7 @@ declare function setIdPrefix(prefix: string): void;
89
94
  * @example
90
95
  * ```ts
91
96
  * const id = id();
92
- * label({ htmlFor: id, nodes: "Name" });
97
+ * label({ htmlFor: id }, "Name");
93
98
  * input({ id, type: "text" });
94
99
  * ```
95
100
  */
@@ -33,9 +33,11 @@ import {
33
33
  transitionState,
34
34
  uniqueId,
35
35
  yieldToMain
36
- } from "./chunk-TSOKIX5Z.js";
37
- import "./chunk-WZSPOOER.js";
38
- import "./chunk-ZD6OAMTH.js";
36
+ } from "./chunk-BMPL52BF.js";
37
+ import "./chunk-VQNQZCWJ.js";
38
+ import "./chunk-EUZND3CB.js";
39
+ import "./chunk-NZIIMDWI.js";
40
+ import "./chunk-K4G4ZQNR.js";
39
41
  import "./chunk-5X6PP2UK.js";
40
42
  export {
41
43
  DOMPool,