sibujs 4.6.0 → 4.7.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 +1 -1
  2. package/dist/browser.cjs +18 -5
  3. package/dist/browser.js +4 -4
  4. package/dist/build.cjs +1956 -1007
  5. package/dist/build.d.cts +133 -44
  6. package/dist/build.d.ts +133 -44
  7. package/dist/build.js +1368 -569
  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-SXXVZMKZ.js → chunk-2BPG2XDA.js} +5 -5
  13. package/dist/{chunk-BAAG6ZTI.js → chunk-3QWBSL5R.js} +4 -4
  14. package/dist/{chunk-5MT6SJ3P.js → chunk-4AWA2PVD.js} +2 -2
  15. package/dist/{chunk-JSXPZCET.js → chunk-7LUJQAOJ.js} +1 -1
  16. package/dist/{chunk-3ISI6ACU.js → chunk-BC2SECJD.js} +1 -1
  17. package/dist/{chunk-DKGBBKOF.js → chunk-BSY63EM6.js} +2 -2
  18. package/dist/{chunk-7NCARGJW.js → chunk-DKXRACVN.js} +3 -3
  19. package/dist/{chunk-47M47FOM.js → chunk-FNJXNGYZ.js} +1 -1
  20. package/dist/{chunk-D33YTSX5.js → chunk-FQRUXCEE.js} +5 -4
  21. package/dist/{chunk-5Q4R7HCL.js → chunk-JWKYU5GV.js} +11 -2
  22. package/dist/{chunk-QE4TTDU3.js → chunk-RUSSKG6G.js} +12 -11
  23. package/dist/{chunk-UZQ6ALFS.js → chunk-SLM3IA34.js} +1 -1
  24. package/dist/{chunk-XRRZKZYX.js → chunk-UNWRJRKC.js} +1 -1
  25. package/dist/{chunk-VVWPJ543.js → chunk-UOL2ECCS.js} +40 -11
  26. package/dist/{chunk-ORMZXBKQ.js → chunk-VZSG24LS.js} +3 -3
  27. package/dist/{chunk-XC4MEKGA.js → chunk-WEQ3DMVL.js} +8 -2
  28. package/dist/{chunk-PK6FK2G2.js → chunk-WOLJZUFQ.js} +176 -27
  29. package/dist/{chunk-7GQCWFOE.js → chunk-XYV3EDB7.js} +143 -74
  30. package/dist/{chunk-AIF3Z2T7.js → chunk-XZR4PXRE.js} +3 -3
  31. package/dist/{chunk-HYXDKS4N.js → chunk-Z3OHK6QT.js} +4 -4
  32. package/dist/{chunk-OXUY2A6L.js → chunk-ZVL7TY4K.js} +2 -2
  33. package/dist/{contracts-DRIuclVT.d.ts → contracts-CLqzJnOV.d.ts} +28 -17
  34. package/dist/{contracts-DRIuclVT.d.cts → contracts-CTOJXu-x.d.cts} +28 -17
  35. package/dist/data.cjs +198 -39
  36. package/dist/data.d.cts +119 -3
  37. package/dist/data.d.ts +119 -3
  38. package/dist/data.js +5 -5
  39. package/dist/devtools.cjs +18 -5
  40. package/dist/devtools.js +4 -4
  41. package/dist/ecosystem.cjs +64 -23
  42. package/dist/ecosystem.d.cts +2 -1
  43. package/dist/ecosystem.d.ts +2 -1
  44. package/dist/ecosystem.js +7 -7
  45. package/dist/extras.cjs +236 -49
  46. package/dist/extras.d.cts +3 -2
  47. package/dist/extras.d.ts +3 -2
  48. package/dist/extras.js +20 -20
  49. package/dist/index.cjs +259 -108
  50. package/dist/index.d.cts +193 -168
  51. package/dist/index.d.ts +193 -168
  52. package/dist/index.js +10 -10
  53. package/dist/motion.cjs +12 -4
  54. package/dist/motion.js +3 -3
  55. package/dist/patterns.cjs +28 -15
  56. package/dist/patterns.d.cts +2 -1
  57. package/dist/patterns.d.ts +2 -1
  58. package/dist/patterns.js +5 -5
  59. package/dist/performance.cjs +18 -5
  60. package/dist/performance.js +4 -4
  61. package/dist/plugins.cjs +118 -34
  62. package/dist/plugins.d.cts +124 -11
  63. package/dist/plugins.d.ts +124 -11
  64. package/dist/plugins.js +68 -26
  65. package/dist/ssr.cjs +54 -13
  66. package/dist/ssr.js +7 -7
  67. package/dist/{tagFactory-DFstCLQV.d.cts → tagFactory-8qL9LCIx.d.cts} +58 -19
  68. package/dist/{tagFactory-DkaNVUNV.d.ts → tagFactory-BL2fymez.d.ts} +58 -19
  69. package/dist/testing.cjs +15 -2
  70. package/dist/testing.js +2 -2
  71. package/dist/types-CJFViL6Q.d.cts +26 -0
  72. package/dist/types-CJFViL6Q.d.ts +26 -0
  73. package/dist/ui.cjs +28 -15
  74. package/dist/ui.d.cts +2 -1
  75. package/dist/ui.d.ts +2 -1
  76. package/dist/ui.js +7 -7
  77. package/dist/widgets.cjs +22 -14
  78. package/dist/widgets.js +6 -6
  79. package/package.json +4 -2
package/dist/data.d.cts CHANGED
@@ -57,6 +57,43 @@ declare function calculateDelay(attempt: number, strategy: "exponential" | "line
57
57
  */
58
58
  declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions, onRetry?: (error: unknown, attempt: number, delay: number) => void, signal?: AbortSignal): Promise<T>;
59
59
 
60
+ /**
61
+ * Structural sharing for the data layer.
62
+ *
63
+ * A refetch almost always produces a brand-new object graph, even when the
64
+ * server sent back exactly what it sent last time. Committing that graph to a
65
+ * signal notifies every subscriber — signals compare with `Object.is` — so a
66
+ * background refresh that changed nothing still re-ran every binding reading
67
+ * the data, and a `when(() => q.data(), …)` or a `() => q.data() && Form()`
68
+ * child rebuilt its subtree and threw away whatever the user was typing.
69
+ *
70
+ * `replaceEqualDeep(prev, next)` reconciles the new graph against the old one:
71
+ *
72
+ * - deeply equal → returns `prev` itself, so the signal write is a no-op and
73
+ * nothing is notified;
74
+ * - partially equal → returns a new container in which every unchanged
75
+ * subtree is the OLD reference, so an `each()` keyed by reference, a
76
+ * `derived` over one branch, or an `equals`-less signal holding a sub-object
77
+ * stays stable while only the changed path gets fresh identities.
78
+ *
79
+ * Only plain objects (prototype `Object.prototype` or `null`) and arrays are
80
+ * reconciled. Everything else — Date, Map, Set, class instances — is opaque
81
+ * and compared by identity: its enumerable keys say nothing about its state,
82
+ * and rebuilding it as a plain object would change its type.
83
+ *
84
+ * Internal to the data layer; not re-exported from `data.ts`.
85
+ */
86
+ /**
87
+ * The public knob shared by `query()` and `resource()`.
88
+ *
89
+ * - `true` (default) — reconcile with {@link replaceEqualDeep}.
90
+ * - `false` — commit every result as-is (a fresh reference notifies).
91
+ * - a function — custom reconciliation. It receives the previously committed
92
+ * value and the new one and returns what to commit; returning `prev` means
93
+ * "unchanged" and notifies nobody.
94
+ */
95
+ type StructuralSharingOption<T> = boolean | ((prev: T, next: T) => T);
96
+
60
97
  /**
61
98
  * ## Callback semantics
62
99
  *
@@ -118,8 +155,52 @@ interface QueryOptions<T> {
118
155
  onError?: (error: Error) => void;
119
156
  /** Called on fetch settle (success or error) */
120
157
  onSettled?: () => void;
121
- /** Transform fetched data before returning to consumers. Cache stores raw data. */
158
+ /**
159
+ * Transform fetched data before returning to consumers. Cache stores raw data.
160
+ *
161
+ * Runs in this observer's own reactive computation: it re-runs when the
162
+ * cached value changes, and when a signal it reads changes — without
163
+ * re-running the key effect, so such a signal never triggers a refetch. It
164
+ * runs once per fetch that changed the data, and its output is structurally
165
+ * shared against this observer's previous output for the same key, so an
166
+ * identical refetch notifies nobody.
167
+ */
122
168
  select?: (data: T) => T;
169
+ /**
170
+ * Keep data referentially stable across refetches. Default: `true`.
171
+ *
172
+ * Every refetch — `refetchInterval`, window focus, reconnect,
173
+ * `invalidateQueries` — produces a new object graph even when the server
174
+ * returned the same thing. With structural sharing on, the new result is
175
+ * reconciled against the previous value of the same key: if it is deeply
176
+ * equal the previous reference is kept and `data` subscribers are not
177
+ * notified at all; if only part of it changed, every unchanged nested object
178
+ * or array keeps its old reference, so `each()` rows and deriveds over
179
+ * untouched branches stay put. Only plain objects and arrays are compared —
180
+ * Date, Map, Set and class instances compare by identity. Cyclic data is
181
+ * safe: the containers on a cycle are taken as-is.
182
+ *
183
+ * Every observer of a key using the default holds the SAME reference.
184
+ *
185
+ * `setQueryData()` is an explicit write, not a fetch: when it hands over a
186
+ * new top-level reference, observers always receive a new top-level
187
+ * reference and are notified — even if every child is unchanged, as after
188
+ * `prev.items.push(x); return { ...prev }`. Unchanged nested subtrees are
189
+ * still reused beneath it.
190
+ *
191
+ * The option applies to this observer only; observers of one key may use
192
+ * different settings.
193
+ *
194
+ * - `false` commits every result as-is (each refetch notifies).
195
+ * - A function `(prev, next) => T` replaces the default reconciliation;
196
+ * `prev` is this observer's previous value for the same key. Return `prev`
197
+ * to report "unchanged" (ignored for an explicit write of a new
198
+ * reference). It must not throw — if it does, the error is reported and
199
+ * `next` is committed unshared.
200
+ *
201
+ * Also applied to this observer's `select` output.
202
+ */
203
+ structuralSharing?: StructuralSharingOption<T>;
123
204
  }
124
205
  interface QueryResult<T> {
125
206
  /** Reactive getter for the cached data */
@@ -143,9 +224,20 @@ declare function query<T>(key: string | (() => string), fetcher: (ctx: {
143
224
  }) => Promise<T>, options?: QueryOptions<T>): QueryResult<T>;
144
225
  /** Invalidate queries matching a key or predicate, triggering refetch for active subscribers */
145
226
  declare function invalidateQueries(keyOrPredicate: string | ((key: string) => boolean)): void;
146
- /** Get cached data for a query key */
227
+ /**
228
+ * Get cached data for a query key — the same reference every observer using
229
+ * the default `structuralSharing` holds.
230
+ */
147
231
  declare function getQueryData<T>(key: string): T | undefined;
148
- /** Set cached data for a query key, notifying subscribers */
232
+ /**
233
+ * Set cached data for a query key, notifying subscribers.
234
+ *
235
+ * An explicit write: a value (or updater result) that is a new top-level
236
+ * reference reaches every observer as a new top-level reference, even when it
237
+ * is deeply equal to the previous value — so `prev.items.push(x); return
238
+ * { ...prev }` is never dropped. Unchanged nested subtrees are still reused.
239
+ * Returning `prev` itself changes nothing.
240
+ */
149
241
  declare function setQueryData<T>(key: string, data: T | ((prev: T | undefined) => T)): void;
150
242
  /** Clear the entire query cache */
151
243
  declare function clearQueryCache(): void;
@@ -354,6 +446,30 @@ interface ResourceOptions<T> {
354
446
  onError?: (error: Error) => void;
355
447
  /** Called on fetch settle (success or error) */
356
448
  onSettled?: () => void;
449
+ /**
450
+ * Keep data referentially stable across refetches. Default: `true`.
451
+ *
452
+ * Each fetched result — from `refetch()` or a source change — is
453
+ * reconciled against the value already held: a deeply equal result keeps
454
+ * the previous reference and notifies nobody, and a partially changed one
455
+ * reuses every unchanged nested object or array. Only plain objects and
456
+ * arrays are compared; Date, Map, Set and class instances compare by
457
+ * identity. Cyclic data is safe: the containers on a cycle are taken as-is.
458
+ * Same contract as `QueryOptions.structuralSharing`.
459
+ *
460
+ * `mutate()` is an explicit write, not a fetch: when it hands over a new
461
+ * top-level reference, `data` always becomes a new top-level reference and
462
+ * notifies — even if every child is unchanged, as after
463
+ * `prev.items.push(x); return { ...prev }`. Unchanged nested subtrees are
464
+ * still reused beneath it.
465
+ *
466
+ * - `false` commits every result as-is.
467
+ * - A function `(prev, next) => T` replaces the default reconciliation;
468
+ * return `prev` to report "unchanged" (ignored for a `mutate()` of a new
469
+ * reference). If it throws, the error is reported and `next` is committed
470
+ * unshared.
471
+ */
472
+ structuralSharing?: StructuralSharingOption<T>;
357
473
  }
358
474
  interface Resource<T> {
359
475
  /** Reactive getter for the fetched data */
package/dist/data.d.ts CHANGED
@@ -57,6 +57,43 @@ declare function calculateDelay(attempt: number, strategy: "exponential" | "line
57
57
  */
58
58
  declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions, onRetry?: (error: unknown, attempt: number, delay: number) => void, signal?: AbortSignal): Promise<T>;
59
59
 
60
+ /**
61
+ * Structural sharing for the data layer.
62
+ *
63
+ * A refetch almost always produces a brand-new object graph, even when the
64
+ * server sent back exactly what it sent last time. Committing that graph to a
65
+ * signal notifies every subscriber — signals compare with `Object.is` — so a
66
+ * background refresh that changed nothing still re-ran every binding reading
67
+ * the data, and a `when(() => q.data(), …)` or a `() => q.data() && Form()`
68
+ * child rebuilt its subtree and threw away whatever the user was typing.
69
+ *
70
+ * `replaceEqualDeep(prev, next)` reconciles the new graph against the old one:
71
+ *
72
+ * - deeply equal → returns `prev` itself, so the signal write is a no-op and
73
+ * nothing is notified;
74
+ * - partially equal → returns a new container in which every unchanged
75
+ * subtree is the OLD reference, so an `each()` keyed by reference, a
76
+ * `derived` over one branch, or an `equals`-less signal holding a sub-object
77
+ * stays stable while only the changed path gets fresh identities.
78
+ *
79
+ * Only plain objects (prototype `Object.prototype` or `null`) and arrays are
80
+ * reconciled. Everything else — Date, Map, Set, class instances — is opaque
81
+ * and compared by identity: its enumerable keys say nothing about its state,
82
+ * and rebuilding it as a plain object would change its type.
83
+ *
84
+ * Internal to the data layer; not re-exported from `data.ts`.
85
+ */
86
+ /**
87
+ * The public knob shared by `query()` and `resource()`.
88
+ *
89
+ * - `true` (default) — reconcile with {@link replaceEqualDeep}.
90
+ * - `false` — commit every result as-is (a fresh reference notifies).
91
+ * - a function — custom reconciliation. It receives the previously committed
92
+ * value and the new one and returns what to commit; returning `prev` means
93
+ * "unchanged" and notifies nobody.
94
+ */
95
+ type StructuralSharingOption<T> = boolean | ((prev: T, next: T) => T);
96
+
60
97
  /**
61
98
  * ## Callback semantics
62
99
  *
@@ -118,8 +155,52 @@ interface QueryOptions<T> {
118
155
  onError?: (error: Error) => void;
119
156
  /** Called on fetch settle (success or error) */
120
157
  onSettled?: () => void;
121
- /** Transform fetched data before returning to consumers. Cache stores raw data. */
158
+ /**
159
+ * Transform fetched data before returning to consumers. Cache stores raw data.
160
+ *
161
+ * Runs in this observer's own reactive computation: it re-runs when the
162
+ * cached value changes, and when a signal it reads changes — without
163
+ * re-running the key effect, so such a signal never triggers a refetch. It
164
+ * runs once per fetch that changed the data, and its output is structurally
165
+ * shared against this observer's previous output for the same key, so an
166
+ * identical refetch notifies nobody.
167
+ */
122
168
  select?: (data: T) => T;
169
+ /**
170
+ * Keep data referentially stable across refetches. Default: `true`.
171
+ *
172
+ * Every refetch — `refetchInterval`, window focus, reconnect,
173
+ * `invalidateQueries` — produces a new object graph even when the server
174
+ * returned the same thing. With structural sharing on, the new result is
175
+ * reconciled against the previous value of the same key: if it is deeply
176
+ * equal the previous reference is kept and `data` subscribers are not
177
+ * notified at all; if only part of it changed, every unchanged nested object
178
+ * or array keeps its old reference, so `each()` rows and deriveds over
179
+ * untouched branches stay put. Only plain objects and arrays are compared —
180
+ * Date, Map, Set and class instances compare by identity. Cyclic data is
181
+ * safe: the containers on a cycle are taken as-is.
182
+ *
183
+ * Every observer of a key using the default holds the SAME reference.
184
+ *
185
+ * `setQueryData()` is an explicit write, not a fetch: when it hands over a
186
+ * new top-level reference, observers always receive a new top-level
187
+ * reference and are notified — even if every child is unchanged, as after
188
+ * `prev.items.push(x); return { ...prev }`. Unchanged nested subtrees are
189
+ * still reused beneath it.
190
+ *
191
+ * The option applies to this observer only; observers of one key may use
192
+ * different settings.
193
+ *
194
+ * - `false` commits every result as-is (each refetch notifies).
195
+ * - A function `(prev, next) => T` replaces the default reconciliation;
196
+ * `prev` is this observer's previous value for the same key. Return `prev`
197
+ * to report "unchanged" (ignored for an explicit write of a new
198
+ * reference). It must not throw — if it does, the error is reported and
199
+ * `next` is committed unshared.
200
+ *
201
+ * Also applied to this observer's `select` output.
202
+ */
203
+ structuralSharing?: StructuralSharingOption<T>;
123
204
  }
124
205
  interface QueryResult<T> {
125
206
  /** Reactive getter for the cached data */
@@ -143,9 +224,20 @@ declare function query<T>(key: string | (() => string), fetcher: (ctx: {
143
224
  }) => Promise<T>, options?: QueryOptions<T>): QueryResult<T>;
144
225
  /** Invalidate queries matching a key or predicate, triggering refetch for active subscribers */
145
226
  declare function invalidateQueries(keyOrPredicate: string | ((key: string) => boolean)): void;
146
- /** Get cached data for a query key */
227
+ /**
228
+ * Get cached data for a query key — the same reference every observer using
229
+ * the default `structuralSharing` holds.
230
+ */
147
231
  declare function getQueryData<T>(key: string): T | undefined;
148
- /** Set cached data for a query key, notifying subscribers */
232
+ /**
233
+ * Set cached data for a query key, notifying subscribers.
234
+ *
235
+ * An explicit write: a value (or updater result) that is a new top-level
236
+ * reference reaches every observer as a new top-level reference, even when it
237
+ * is deeply equal to the previous value — so `prev.items.push(x); return
238
+ * { ...prev }` is never dropped. Unchanged nested subtrees are still reused.
239
+ * Returning `prev` itself changes nothing.
240
+ */
149
241
  declare function setQueryData<T>(key: string, data: T | ((prev: T | undefined) => T)): void;
150
242
  /** Clear the entire query cache */
151
243
  declare function clearQueryCache(): void;
@@ -354,6 +446,30 @@ interface ResourceOptions<T> {
354
446
  onError?: (error: Error) => void;
355
447
  /** Called on fetch settle (success or error) */
356
448
  onSettled?: () => void;
449
+ /**
450
+ * Keep data referentially stable across refetches. Default: `true`.
451
+ *
452
+ * Each fetched result — from `refetch()` or a source change — is
453
+ * reconciled against the value already held: a deeply equal result keeps
454
+ * the previous reference and notifies nobody, and a partially changed one
455
+ * reuses every unchanged nested object or array. Only plain objects and
456
+ * arrays are compared; Date, Map, Set and class instances compare by
457
+ * identity. Cyclic data is safe: the containers on a cycle are taken as-is.
458
+ * Same contract as `QueryOptions.structuralSharing`.
459
+ *
460
+ * `mutate()` is an explicit write, not a fetch: when it hands over a new
461
+ * top-level reference, `data` always becomes a new top-level reference and
462
+ * notifies — even if every child is unchanged, as after
463
+ * `prev.items.push(x); return { ...prev }`. Unchanged nested subtrees are
464
+ * still reused beneath it.
465
+ *
466
+ * - `false` commits every result as-is.
467
+ * - A function `(prev, next) => T` replaces the default reconciliation;
468
+ * return `prev` to report "unchanged" (ignored for a `mutate()` of a new
469
+ * reference). If it throws, the error is reported and `next` is committed
470
+ * unshared.
471
+ */
472
+ structuralSharing?: StructuralSharingOption<T>;
357
473
  }
358
474
  interface Resource<T> {
359
475
  /** Reactive getter for the fetched data */
package/dist/data.js CHANGED
@@ -22,14 +22,14 @@ import {
22
22
  throttle,
23
23
  withLoader,
24
24
  withRetry
25
- } from "./chunk-PK6FK2G2.js";
26
- import "./chunk-QE4TTDU3.js";
25
+ } from "./chunk-WOLJZUFQ.js";
26
+ import "./chunk-RUSSKG6G.js";
27
27
  import "./chunk-2INLLLMZ.js";
28
28
  import "./chunk-7ZHH77QA.js";
29
- import "./chunk-XC4MEKGA.js";
29
+ import "./chunk-WEQ3DMVL.js";
30
30
  import "./chunk-7XHATCIH.js";
31
- import "./chunk-D33YTSX5.js";
32
- import "./chunk-5Q4R7HCL.js";
31
+ import "./chunk-FQRUXCEE.js";
32
+ import "./chunk-JWKYU5GV.js";
33
33
  import "./chunk-NYNYSPK7.js";
34
34
  export {
35
35
  __resetQueryCache,
package/dist/devtools.cjs CHANGED
@@ -844,7 +844,7 @@ function forEachSubscriber(signal2, visit) {
844
844
  }
845
845
 
846
846
  // src/reactivity/track.ts
847
- var _runtimeVersion = true ? "4.6.0" : "dev";
847
+ var _runtimeVersion = true ? "4.7.0" : "dev";
848
848
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
849
849
  function resolveReactiveApi() {
850
850
  const g = globalThis;
@@ -950,6 +950,14 @@ var batch = API2.batch;
950
950
  var enqueueBatchedSignal = API2.enqueueBatchedSignal;
951
951
  var isBatching = API2.isBatching;
952
952
 
953
+ // src/core/devtoolsHook.ts
954
+ function emitDevtools(hook, event, payload) {
955
+ try {
956
+ hook.emit(event, payload);
957
+ } catch {
958
+ }
959
+ }
960
+
953
961
  // src/core/signals/signal.ts
954
962
  var _g = globalThis;
955
963
  function signal(initial, options) {
@@ -983,7 +991,7 @@ function signal(initial, options) {
983
991
  state2.__v++;
984
992
  if (DEV) {
985
993
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
986
- if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
994
+ if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
987
995
  }
988
996
  if (!enqueueBatchedSignal(state2)) {
989
997
  notifySubscribers2(state2);
@@ -997,7 +1005,7 @@ function signal(initial, options) {
997
1005
  state2.value = newValue;
998
1006
  state2.__v++;
999
1007
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1000
- if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
1008
+ if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
1001
1009
  if (!enqueueBatchedSignal(state2)) {
1002
1010
  notifySubscribers2(state2);
1003
1011
  }
@@ -1016,7 +1024,7 @@ function signal(initial, options) {
1016
1024
  }
1017
1025
  if (DEV) {
1018
1026
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1019
- if (hook) hook.emit("signal:create", { signal: state2, name: debugName, getter: get, initial });
1027
+ if (hook) emitDevtools(hook, "signal:create", { signal: state2, name: debugName, getter: get, initial });
1020
1028
  }
1021
1029
  return [get, set];
1022
1030
  }
@@ -2164,12 +2172,17 @@ function effect(effectFn, options) {
2164
2172
  try {
2165
2173
  retrack2(ctx.bodyFn, ctx.subscriber);
2166
2174
  if (ctx.rerunPending) drainReruns(ctx);
2175
+ } catch (err) {
2176
+ ctx.disposed = true;
2177
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
2178
+ cleanup2(ctx.subscriber);
2179
+ throw err;
2167
2180
  } finally {
2168
2181
  ctx.running = false;
2169
2182
  ctx.rerunPending = false;
2170
2183
  }
2171
2184
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
2172
- if (hook) hook.emit("effect:create", { effectFn });
2185
+ if (hook) emitDevtools(hook, "effect:create", { effectFn });
2173
2186
  return () => disposeEffect(ctx);
2174
2187
  }
2175
2188
 
package/dist/devtools.js CHANGED
@@ -35,13 +35,13 @@ import {
35
35
  trackCleanup,
36
36
  walkDependencyGraph,
37
37
  withErrorTracking
38
- } from "./chunk-7NCARGJW.js";
38
+ } from "./chunk-DKXRACVN.js";
39
39
  import "./chunk-2INLLLMZ.js";
40
40
  import "./chunk-7ZHH77QA.js";
41
- import "./chunk-XC4MEKGA.js";
41
+ import "./chunk-WEQ3DMVL.js";
42
42
  import "./chunk-7XHATCIH.js";
43
- import "./chunk-D33YTSX5.js";
44
- import "./chunk-5Q4R7HCL.js";
43
+ import "./chunk-FQRUXCEE.js";
44
+ import "./chunk-JWKYU5GV.js";
45
45
  import {
46
46
  isDev
47
47
  } from "./chunk-NYNYSPK7.js";
@@ -716,7 +716,7 @@ function forEachSubscriber(signal2, visit) {
716
716
  }
717
717
 
718
718
  // src/reactivity/track.ts
719
- var _runtimeVersion = true ? "4.6.0" : "dev";
719
+ var _runtimeVersion = true ? "4.7.0" : "dev";
720
720
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
721
721
  function resolveReactiveApi() {
722
722
  const g = globalThis;
@@ -772,6 +772,14 @@ var getSubscriberCount2 = API.getSubscriberCount;
772
772
  var getSubscriberDeps2 = API.getSubscriberDeps;
773
773
  var forEachSubscriber2 = API.forEachSubscriber;
774
774
 
775
+ // src/core/devtoolsHook.ts
776
+ function emitDevtools(hook, event, payload) {
777
+ try {
778
+ hook.emit(event, payload);
779
+ } catch {
780
+ }
781
+ }
782
+
775
783
  // src/core/ssr-context.ts
776
784
  var SSR_KEY = /* @__PURE__ */ Symbol.for("sibujs.ssr.v1");
777
785
  function detectSSRShared() {
@@ -926,12 +934,17 @@ function effect(effectFn, options) {
926
934
  try {
927
935
  retrack2(ctx.bodyFn, ctx.subscriber);
928
936
  if (ctx.rerunPending) drainReruns(ctx);
937
+ } catch (err) {
938
+ ctx.disposed = true;
939
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
940
+ cleanup2(ctx.subscriber);
941
+ throw err;
929
942
  } finally {
930
943
  ctx.running = false;
931
944
  ctx.rerunPending = false;
932
945
  }
933
946
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
934
- if (hook) hook.emit("effect:create", { effectFn });
947
+ if (hook) emitDevtools(hook, "effect:create", { effectFn });
935
948
  return () => disposeEffect(ctx);
936
949
  }
937
950
 
@@ -1018,7 +1031,7 @@ function signal(initial, options) {
1018
1031
  state2.__v++;
1019
1032
  if (DEV) {
1020
1033
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1021
- if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
1034
+ if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
1022
1035
  }
1023
1036
  if (!enqueueBatchedSignal(state2)) {
1024
1037
  notifySubscribers2(state2);
@@ -1032,7 +1045,7 @@ function signal(initial, options) {
1032
1045
  state2.value = newValue;
1033
1046
  state2.__v++;
1034
1047
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1035
- if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
1048
+ if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
1036
1049
  if (!enqueueBatchedSignal(state2)) {
1037
1050
  notifySubscribers2(state2);
1038
1051
  }
@@ -1051,7 +1064,7 @@ function signal(initial, options) {
1051
1064
  }
1052
1065
  if (DEV) {
1053
1066
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1054
- if (hook) hook.emit("signal:create", { signal: state2, name: debugName, getter: get, initial });
1067
+ if (hook) emitDevtools(hook, "signal:create", { signal: state2, name: debugName, getter: get, initial });
1055
1068
  }
1056
1069
  return [get, set];
1057
1070
  }
@@ -1334,17 +1347,17 @@ function derived(getter, options) {
1334
1347
  cs._d = false;
1335
1348
  cs._init = true;
1336
1349
  };
1337
- track2(() => {
1338
- let threw = true;
1339
- try {
1350
+ try {
1351
+ track2(() => {
1340
1352
  cs._v = getter();
1341
1353
  cs._d = false;
1342
1354
  cs._init = true;
1343
- threw = false;
1344
- } finally {
1345
- if (threw) cs._d = true;
1346
- }
1347
- }, markDirty);
1355
+ }, markDirty);
1356
+ } catch (err) {
1357
+ disposed = true;
1358
+ cleanup2(markDirty);
1359
+ throw err;
1360
+ }
1348
1361
  const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1349
1362
  const validate = () => {
1350
1363
  if (!cs._d || disposed || pendingError !== void 0) return;
@@ -1363,7 +1376,7 @@ function derived(getter, options) {
1363
1376
  if (disposed) cleanup2(markDirty);
1364
1377
  }
1365
1378
  if (hook && !disposed && !Object.is(oldValue, cs._v)) {
1366
- hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
1379
+ emitDevtools(hook, "computed:update", { signal: cs, oldValue, newValue: cs._v });
1367
1380
  }
1368
1381
  };
1369
1382
  cs._validate = validate;
@@ -1414,7 +1427,7 @@ function derived(getter, options) {
1414
1427
  }
1415
1428
  }
1416
1429
  };
1417
- if (hook) hook.emit("computed:create", { signal: cs, name: debugName, getter: computedGetter });
1430
+ if (hook) emitDevtools(hook, "computed:create", { signal: cs, name: debugName, getter: computedGetter });
1418
1431
  return computedGetter;
1419
1432
  }
1420
1433
 
@@ -1983,8 +1996,34 @@ function toKebab(prop) {
1983
1996
  }
1984
1997
  function applyStyle(el, style) {
1985
1998
  if (typeof style === "function") {
1999
+ const getter = style;
2000
+ let written = [];
2001
+ let wroteString = false;
1986
2002
  const teardown = reactiveBinding2(() => {
1987
- el.setAttribute("style", sanitizeStyleAttribute(String(style()), { element: el }));
2003
+ const value = getter();
2004
+ if (value === null || typeof value !== "object") {
2005
+ written = [];
2006
+ wroteString = true;
2007
+ el.setAttribute("style", sanitizeStyleAttribute(String(value), { element: el }));
2008
+ return;
2009
+ }
2010
+ const decl = el.style;
2011
+ if (wroteString) {
2012
+ el.removeAttribute("style");
2013
+ wroteString = false;
2014
+ }
2015
+ const next = [];
2016
+ for (const prop in value) {
2017
+ const val = value[prop];
2018
+ if (val == null) continue;
2019
+ const name = toKebab(prop);
2020
+ next.push(name);
2021
+ decl.setProperty(name, sanitizeCSSValue(String(val), { property: name, element: el }));
2022
+ }
2023
+ for (let i = 0; i < written.length; i++) {
2024
+ if (next.indexOf(written[i]) === -1) decl.removeProperty(written[i]);
2025
+ }
2026
+ written = next;
1988
2027
  }, el);
1989
2028
  registerDisposer(el, teardown);
1990
2029
  return;
@@ -2015,7 +2054,9 @@ function applyClass(el, cls) {
2015
2054
  }
2016
2055
  if (typeof cls === "function") {
2017
2056
  const teardown = reactiveBinding2(() => {
2018
- el.setAttribute("class", cls());
2057
+ const value = cls();
2058
+ if (value == null || value === false) el.removeAttribute("class");
2059
+ else el.setAttribute("class", value);
2019
2060
  }, el);
2020
2061
  registerDisposer(el, teardown);
2021
2062
  return;
@@ -2097,7 +2138,7 @@ function appendChildren(el, nodes) {
2097
2138
  }
2098
2139
  }
2099
2140
  }
2100
- var tagFactory = (tag, ns) => {
2141
+ function tagFactory(tag, ns) {
2101
2142
  const blocked = isBlockedTag(tag);
2102
2143
  return (first, second) => {
2103
2144
  if (blocked) {
@@ -2146,7 +2187,9 @@ var tagFactory = (tag, ns) => {
2146
2187
  const pClass = props.class;
2147
2188
  if (pClass != null) applyClass(el, pClass);
2148
2189
  const pId = props.id;
2149
- if (pId != null) {
2190
+ if (typeof pId === "function") {
2191
+ registerDisposer(el, bindAttribute(el, "id", pId));
2192
+ } else if (pId != null) {
2150
2193
  if (DEV && typeof pId === "string" && CLOBBER_RISKY_IDS.has(pId.toLowerCase())) {
2151
2194
  devWarn(
2152
2195
  `tagFactory: element id="${pId}" matches a common global and may cause DOM clobbering. Avoid setting ids from untrusted input.`
@@ -2196,12 +2239,10 @@ var tagFactory = (tag, ns) => {
2196
2239
  }
2197
2240
  }
2198
2241
  }
2199
- if (props.onElement && typeof props.onElement === "function") {
2200
- props.onElement(el);
2201
- }
2242
+ if (typeof props.onElement === "function") props.onElement(el);
2202
2243
  return el;
2203
2244
  };
2204
- };
2245
+ }
2205
2246
 
2206
2247
  // src/ecosystem/ui/componentAdapter.ts
2207
2248
  var _themeLayers = globalSingleton(
@@ -1,5 +1,6 @@
1
1
  import { S as SibuPlugin } from './plugin-DVgSnTfK.cjs';
2
- import { D as DerivedAccessor, T as TagProps, a as NodeChildren } from './tagFactory-DFstCLQV.cjs';
2
+ import { D as DerivedAccessor, T as TagProps } from './tagFactory-8qL9LCIx.cjs';
3
+ import { N as NodeChildren } from './types-CJFViL6Q.cjs';
3
4
  import './signal-EotCj4hS.cjs';
4
5
 
5
6
  /** MobX autorun disposer function type. */
@@ -1,5 +1,6 @@
1
1
  import { S as SibuPlugin } from './plugin-DVgSnTfK.js';
2
- import { D as DerivedAccessor, T as TagProps, a as NodeChildren } from './tagFactory-DkaNVUNV.js';
2
+ import { D as DerivedAccessor, T as TagProps } from './tagFactory-BL2fymez.js';
3
+ import { N as NodeChildren } from './types-CJFViL6Q.js';
3
4
  import './signal-EotCj4hS.js';
4
5
 
5
6
  /** MobX autorun disposer function type. */
package/dist/ecosystem.js CHANGED
@@ -7,18 +7,18 @@ import {
7
7
  mobXAdapter,
8
8
  reduxAdapter,
9
9
  zustandAdapter
10
- } from "./chunk-BAAG6ZTI.js";
10
+ } from "./chunk-3QWBSL5R.js";
11
11
  import "./chunk-J6FW5TV6.js";
12
- import "./chunk-VVWPJ543.js";
13
- import "./chunk-47M47FOM.js";
14
- import "./chunk-QE4TTDU3.js";
12
+ import "./chunk-UOL2ECCS.js";
13
+ import "./chunk-FNJXNGYZ.js";
14
+ import "./chunk-RUSSKG6G.js";
15
15
  import "./chunk-2INLLLMZ.js";
16
16
  import "./chunk-7ZHH77QA.js";
17
- import "./chunk-XC4MEKGA.js";
17
+ import "./chunk-WEQ3DMVL.js";
18
18
  import "./chunk-7XHATCIH.js";
19
19
  import "./chunk-5HZXGZ6T.js";
20
- import "./chunk-D33YTSX5.js";
21
- import "./chunk-5Q4R7HCL.js";
20
+ import "./chunk-FQRUXCEE.js";
21
+ import "./chunk-JWKYU5GV.js";
22
22
  import "./chunk-NYNYSPK7.js";
23
23
  export {
24
24
  antdAdapter,