sibujs 4.1.0 → 4.2.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 (80) hide show
  1. package/README.md +24 -0
  2. package/dist/browser.cjs +13 -21
  3. package/dist/browser.js +6 -6
  4. package/dist/build.cjs +278 -104
  5. package/dist/build.js +15 -15
  6. package/dist/cdn.dev.global.js +398 -0
  7. package/dist/cdn.global.js +10 -10
  8. package/dist/{chunk-DCDS2PQ6.js → chunk-2LOYNGBY.js} +2 -2
  9. package/dist/{chunk-VPP2FONR.js → chunk-2WLZ6757.js} +17 -10
  10. package/dist/{chunk-IM7N5WR4.js → chunk-3VTSPNC5.js} +2 -2
  11. package/dist/{chunk-TIRZCERI.js → chunk-5DXA2J44.js} +1 -1
  12. package/dist/{chunk-I3VI34DP.js → chunk-6ZGI3RBE.js} +149 -33
  13. package/dist/{chunk-D3SZ4LVS.js → chunk-7ALC7KGE.js} +5 -6
  14. package/dist/{chunk-PFTDGEPZ.js → chunk-7VEC3UD2.js} +3 -3
  15. package/dist/{chunk-KSOEJGFO.js → chunk-BMYYZWHX.js} +3 -3
  16. package/dist/{chunk-LOFZHP6V.js → chunk-BNKV4REK.js} +7 -8
  17. package/dist/{chunk-5GJ5RTHA.js → chunk-FOWBJZDL.js} +7 -8
  18. package/dist/{chunk-FB7KQXZI.js → chunk-GDUNQCCW.js} +4 -4
  19. package/dist/{chunk-UFSQEO7P.js → chunk-HECVW343.js} +7 -8
  20. package/dist/{chunk-SHDVWATN.js → chunk-HJEPKZ2O.js} +7 -8
  21. package/dist/{chunk-ANGSYNPX.js → chunk-HZEBO22J.js} +57 -37
  22. package/dist/{chunk-5INI7D2L.js → chunk-IXKSNWV5.js} +74 -11
  23. package/dist/{chunk-DAKLOMKS.js → chunk-KE4KWRDY.js} +3 -3
  24. package/dist/{chunk-AW6HPJ3L.js → chunk-KKLW7YWL.js} +1 -1
  25. package/dist/{chunk-BMSN6NWQ.js → chunk-NEYWS4EX.js} +6 -6
  26. package/dist/{chunk-HYCCIYNS.js → chunk-NIOYEGBQ.js} +13 -14
  27. package/dist/{chunk-4U7KD3RY.js → chunk-OUWAIXIK.js} +3 -3
  28. package/dist/{chunk-MRAMKDHQ.js → chunk-P3DHXBR6.js} +1 -1
  29. package/dist/{chunk-FMHIQNMP.js → chunk-QCFLDD7C.js} +5 -5
  30. package/dist/{chunk-DHDZ7IVN.js → chunk-QKRPLZ2V.js} +7 -8
  31. package/dist/{chunk-4RH6D7GR.js → chunk-QL4UREZO.js} +6 -6
  32. package/dist/{chunk-XIHMS2Y6.js → chunk-SF3TB4K6.js} +8 -8
  33. package/dist/{chunk-XSZRJW76.js → chunk-XZ2N525U.js} +6 -6
  34. package/dist/{customElement-CKLoO63w.d.ts → customElement-CNZxEB9G.d.cts} +77 -0
  35. package/dist/{customElement-CKLoO63w.d.cts → customElement-CNZxEB9G.d.ts} +77 -0
  36. package/dist/data.cjs +15 -24
  37. package/dist/data.js +9 -9
  38. package/dist/devtools.cjs +18 -22
  39. package/dist/devtools.d.cts +1 -1
  40. package/dist/devtools.d.ts +1 -1
  41. package/dist/devtools.js +8 -8
  42. package/dist/{dispose-4FZ6OFXR.js → dispose-46BOMMQJ.js} +2 -2
  43. package/dist/ecosystem.cjs +222 -67
  44. package/dist/ecosystem.js +11 -11
  45. package/dist/extras.cjs +243 -86
  46. package/dist/extras.d.cts +2 -2
  47. package/dist/extras.d.ts +2 -2
  48. package/dist/extras.js +25 -25
  49. package/dist/index.cjs +272 -98
  50. package/dist/index.d.cts +154 -33
  51. package/dist/index.d.ts +154 -33
  52. package/dist/index.js +15 -15
  53. package/dist/{introspect-RjLfIFpL.d.cts → introspect-D5xF-0Es.d.cts} +7 -0
  54. package/dist/{introspect-DOZfmC-4.d.ts → introspect-rSBIxEQi.d.ts} +7 -0
  55. package/dist/motion.cjs +13 -22
  56. package/dist/motion.js +5 -5
  57. package/dist/patterns.cjs +16 -24
  58. package/dist/patterns.d.cts +8 -0
  59. package/dist/patterns.d.ts +8 -0
  60. package/dist/patterns.js +7 -7
  61. package/dist/performance.cjs +16 -27
  62. package/dist/performance.js +9 -9
  63. package/dist/plugins.cjs +260 -86
  64. package/dist/plugins.d.cts +92 -0
  65. package/dist/plugins.d.ts +92 -0
  66. package/dist/plugins.js +18 -17
  67. package/dist/{ssr-JFP5535P.js → ssr-Y7XOEPEN.js} +5 -5
  68. package/dist/ssr.cjs +233 -79
  69. package/dist/ssr.js +12 -12
  70. package/dist/testing.cjs +11 -19
  71. package/dist/testing.js +5 -5
  72. package/dist/ui.cjs +98 -43
  73. package/dist/ui.d.cts +7 -1
  74. package/dist/ui.d.ts +7 -1
  75. package/dist/ui.js +10 -10
  76. package/dist/widgets.cjs +13 -21
  77. package/dist/widgets.d.cts +8 -0
  78. package/dist/widgets.d.ts +8 -0
  79. package/dist/widgets.js +9 -9
  80. package/package.json +5 -2
package/dist/testing.cjs CHANGED
@@ -67,23 +67,17 @@ __export(testing_exports, {
67
67
  module.exports = __toCommonJS(testing_exports);
68
68
 
69
69
  // src/core/dev.ts
70
- function isDev() {
71
- return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
72
- // The bare `__SIBU_DEV__` is a bundler define that only exists in
73
- // production builds; under the test runner it is always undefined, so
74
- // this branch is unreachable here.
75
- /* v8 ignore next 2 */
76
- typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
77
- );
78
- }
79
- var _isDev = isDev();
70
+ var DEV = typeof __SIBU_DEV__ !== "undefined" ? !!__SIBU_DEV__ : typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
71
+ // safe default: off in browser, on in test/dev Node
72
+ typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
73
+ );
80
74
  function devAssert(condition, message) {
81
- if (_isDev && !condition) {
75
+ if ((typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) && !condition) {
82
76
  throw new Error(`[SibuJS] ${message}`);
83
77
  }
84
78
  }
85
79
  function devWarn(message) {
86
- if (_isDev) {
80
+ if (typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) {
87
81
  console.warn(`[SibuJS] ${message}`);
88
82
  }
89
83
  }
@@ -186,7 +180,6 @@ function reportDrainRunaway(label, executed, remaining) {
186
180
  { executed, remaining }
187
181
  );
188
182
  }
189
- var _isDev2 = isDev();
190
183
  var activeBindingCount = 0;
191
184
  function dispose(node) {
192
185
  const stack = [node];
@@ -209,13 +202,13 @@ function dispose(node) {
209
202
  if (!pending || pending.length === 0) break;
210
203
  const snapshot = pending.slice();
211
204
  elementDisposers.delete(current);
212
- if (_isDev2) activeBindingCount -= snapshot.length;
205
+ if (DEV) activeBindingCount -= snapshot.length;
213
206
  for (let i2 = 0; i2 < snapshot.length; i2++) {
214
207
  if (executed >= MAX_DRAIN_TEARDOWNS) {
215
208
  const rest = snapshot.slice(i2);
216
209
  const added = elementDisposers.get(current);
217
210
  elementDisposers.set(current, added ? rest.concat(added) : rest);
218
- if (_isDev2) activeBindingCount += rest.length;
211
+ if (DEV) activeBindingCount += rest.length;
219
212
  reportDrainRunaway("dispose", executed, rest.length + (added?.length ?? 0));
220
213
  runaway = true;
221
214
  break;
@@ -2137,17 +2130,16 @@ function forEachSubscriber(signal, visit) {
2137
2130
  }
2138
2131
 
2139
2132
  // src/reactivity/track.ts
2140
- var _isDev3 = isDev();
2141
- var _runtimeVersion = true ? "4.1.0" : "dev";
2133
+ var _runtimeVersion = true ? "4.2.0" : "dev";
2142
2134
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
2143
2135
  function resolveReactiveApi() {
2144
2136
  const g = globalThis;
2145
2137
  const existing = g[REGISTRY_KEY];
2146
2138
  if (existing) {
2147
- if (_isDev3 && !existing.__dupWarned) {
2139
+ if (DEV && !existing.__dupWarned) {
2148
2140
  existing.__dupWarned = true;
2149
2141
  devWarn(
2150
- `Multiple instances of the reactive runtime detected on this page (active: ${existing.version}, duplicate: ${_runtimeVersion}). Reactivity still works \u2014 all copies share the first one \u2014 but de-duplicate sibujs in your bundler (e.g. Vite optimizeDeps.exclude: ['sibujs'] or resolve.dedupe: ['sibujs']).`
2142
+ `Multiple instances of the reactive runtime detected on this page (active: ${existing.version}, duplicate: ${_runtimeVersion}). Reactivity is NOT broken and no bundler configuration is required: every copy routes through the first one via a global registry, so signal writes still reach subscribers registered by any copy. This is only a SIZE issue \u2014 the duplicate copy's bytes are downloaded and parsed for nothing. If you want them back, de-duplicate sibujs in your bundler (Vite: resolve.dedupe: ['sibujs']). Mismatched VERSIONS above are worth fixing regardless, since only the first one loaded is used.`
2151
2143
  );
2152
2144
  }
2153
2145
  return existing;
package/dist/testing.js CHANGED
@@ -3,13 +3,13 @@ import {
3
3
  } from "./chunk-7ZHH77QA.js";
4
4
  import {
5
5
  effect
6
- } from "./chunk-PFTDGEPZ.js";
7
- import "./chunk-TIRZCERI.js";
6
+ } from "./chunk-7VEC3UD2.js";
7
+ import "./chunk-5DXA2J44.js";
8
8
  import {
9
9
  replaceChildrenSafely
10
- } from "./chunk-DHDZ7IVN.js";
11
- import "./chunk-D3SZ4LVS.js";
12
- import "./chunk-VPP2FONR.js";
10
+ } from "./chunk-QKRPLZ2V.js";
11
+ import "./chunk-7ALC7KGE.js";
12
+ import "./chunk-2WLZ6757.js";
13
13
 
14
14
  // src/testing/a11y.ts
15
15
  function escSel(value) {
package/dist/ui.cjs CHANGED
@@ -86,23 +86,23 @@ __export(ui_exports, {
86
86
  module.exports = __toCommonJS(ui_exports);
87
87
 
88
88
  // src/core/dev.ts
89
- function isDev() {
90
- return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
91
- // The bare `__SIBU_DEV__` is a bundler define that only exists in
92
- // production builds; under the test runner it is always undefined, so
93
- // this branch is unreachable here.
94
- /* v8 ignore next 2 */
95
- typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
96
- );
97
- }
98
- var _isDev = isDev();
89
+ var DEV = typeof __SIBU_DEV__ !== "undefined" ? !!__SIBU_DEV__ : typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
90
+ // safe default: off in browser, on in test/dev Node
91
+ typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
92
+ );
99
93
  function devAssert(condition, message) {
100
- if (_isDev && !condition) {
94
+ if ((typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) && !condition) {
101
95
  throw new Error(`[SibuJS] ${message}`);
102
96
  }
103
97
  }
98
+ function devWarnLazy(build) {
99
+ if (typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) {
100
+ const message = build();
101
+ if (message) console.warn(`[SibuJS] ${message}`);
102
+ }
103
+ }
104
104
  function devWarn(message) {
105
- if (_isDev) {
105
+ if (typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) {
106
106
  console.warn(`[SibuJS] ${message}`);
107
107
  }
108
108
  }
@@ -205,7 +205,6 @@ function reportDrainRunaway(label, executed, remaining) {
205
205
  { executed, remaining }
206
206
  );
207
207
  }
208
- var _isDev2 = isDev();
209
208
  var activeBindingCount = 0;
210
209
  function registerDisposer(node, teardown) {
211
210
  let disposers = elementDisposers.get(node);
@@ -214,7 +213,7 @@ function registerDisposer(node, teardown) {
214
213
  elementDisposers.set(node, disposers);
215
214
  }
216
215
  disposers.push(teardown);
217
- if (_isDev2) activeBindingCount++;
216
+ if (DEV) activeBindingCount++;
218
217
  }
219
218
  function dispose(node) {
220
219
  const stack = [node];
@@ -237,13 +236,13 @@ function dispose(node) {
237
236
  if (!pending || pending.length === 0) break;
238
237
  const snapshot = pending.slice();
239
238
  elementDisposers.delete(current);
240
- if (_isDev2) activeBindingCount -= snapshot.length;
239
+ if (DEV) activeBindingCount -= snapshot.length;
241
240
  for (let i2 = 0; i2 < snapshot.length; i2++) {
242
241
  if (executed >= MAX_DRAIN_TEARDOWNS) {
243
242
  const rest = snapshot.slice(i2);
244
243
  const added = elementDisposers.get(current);
245
244
  elementDisposers.set(current, added ? rest.concat(added) : rest);
246
- if (_isDev2) activeBindingCount += rest.length;
245
+ if (DEV) activeBindingCount += rest.length;
247
246
  reportDrainRunaway("dispose", executed, rest.length + (added?.length ?? 0));
248
247
  runaway = true;
249
248
  break;
@@ -727,17 +726,16 @@ function forEachSubscriber(signal2, visit) {
727
726
  }
728
727
 
729
728
  // src/reactivity/track.ts
730
- var _isDev3 = isDev();
731
- var _runtimeVersion = true ? "4.1.0" : "dev";
729
+ var _runtimeVersion = true ? "4.2.0" : "dev";
732
730
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
733
731
  function resolveReactiveApi() {
734
732
  const g = globalThis;
735
733
  const existing = g[REGISTRY_KEY];
736
734
  if (existing) {
737
- if (_isDev3 && !existing.__dupWarned) {
735
+ if (DEV && !existing.__dupWarned) {
738
736
  existing.__dupWarned = true;
739
737
  devWarn(
740
- `Multiple instances of the reactive runtime detected on this page (active: ${existing.version}, duplicate: ${_runtimeVersion}). Reactivity still works \u2014 all copies share the first one \u2014 but de-duplicate sibujs in your bundler (e.g. Vite optimizeDeps.exclude: ['sibujs'] or resolve.dedupe: ['sibujs']).`
738
+ `Multiple instances of the reactive runtime detected on this page (active: ${existing.version}, duplicate: ${_runtimeVersion}). Reactivity is NOT broken and no bundler configuration is required: every copy routes through the first one via a global registry, so signal writes still reach subscribers registered by any copy. This is only a SIZE issue \u2014 the duplicate copy's bytes are downloaded and parsed for nothing. If you want them back, de-duplicate sibujs in your bundler (Vite: resolve.dedupe: ['sibujs']). Mismatched VERSIONS above are worth fixing regardless, since only the first one loaded is used.`
741
739
  );
742
740
  }
743
741
  return existing;
@@ -1072,7 +1070,6 @@ var isBatching = API2.isBatching;
1072
1070
 
1073
1071
  // src/core/signals/signal.ts
1074
1072
  var _g2 = globalThis;
1075
- var _isDev4 = isDev();
1076
1073
  function signal(initial, options) {
1077
1074
  const state2 = {
1078
1075
  value: initial,
@@ -1085,7 +1082,7 @@ function signal(initial, options) {
1085
1082
  _d: false,
1086
1083
  _validate: void 0
1087
1084
  };
1088
- const debugName = _isDev4 ? options?.name : void 0;
1085
+ const debugName = DEV ? options?.name : void 0;
1089
1086
  const equalsFn = options?.equals;
1090
1087
  if (debugName) state2.__name = debugName;
1091
1088
  function get() {
@@ -1102,7 +1099,7 @@ function signal(initial, options) {
1102
1099
  if (equalsFn(prev, newValue)) return;
1103
1100
  state2.value = newValue;
1104
1101
  state2.__v++;
1105
- if (_isDev4) {
1102
+ if (DEV) {
1106
1103
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1107
1104
  if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
1108
1105
  }
@@ -1110,7 +1107,7 @@ function signal(initial, options) {
1110
1107
  notifySubscribers2(state2);
1111
1108
  }
1112
1109
  };
1113
- } else if (_isDev4) {
1110
+ } else if (DEV) {
1114
1111
  set = (next) => {
1115
1112
  const prev = state2.value;
1116
1113
  const newValue = typeof next === "function" ? next(prev) : next;
@@ -1135,7 +1132,7 @@ function signal(initial, options) {
1135
1132
  }
1136
1133
  };
1137
1134
  }
1138
- if (_isDev4) {
1135
+ if (DEV) {
1139
1136
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1140
1137
  if (hook) hook.emit("signal:create", { signal: state2, name: debugName, getter: get, initial });
1141
1138
  }
@@ -2320,20 +2317,75 @@ function decodeCssEscapes2(value) {
2320
2317
  }
2321
2318
  return out;
2322
2319
  }
2323
- function sanitizeCSSValue(value) {
2324
- if (!CSS_DANGER_GATE.test(value)) return value;
2320
+ var BLOCKED_CSS_NEEDLES = [
2321
+ "url(",
2322
+ "expression(",
2323
+ "javascript:",
2324
+ "vbscript:",
2325
+ "-moz-binding",
2326
+ "behavior:",
2327
+ "@import",
2328
+ "image-set(",
2329
+ "filter:progid"
2330
+ ];
2331
+ function describeElement(el) {
2332
+ if (!el) return "an element";
2333
+ let out = el.tagName.toLowerCase();
2334
+ const id = el.getAttribute("id");
2335
+ if (id) out += `#${id}`;
2336
+ const cls = el.getAttribute("class");
2337
+ if (cls) out += `.${cls.trim().split(/\s+/).slice(0, 3).join(".")}`;
2338
+ return `<${out}>`;
2339
+ }
2340
+ var warnedDeclarations = /* @__PURE__ */ new Set();
2341
+ var MAX_WARNED_DECLARATIONS = 100;
2342
+ var declarationCapAnnounced = false;
2343
+ function warnDroppedDeclaration(value, reason, ctx) {
2344
+ devWarnLazy(() => {
2345
+ const reasons = [
2346
+ "url() in an inline style is an exfiltration channel: the browser fetches it immediately and the URL's path or query can carry page data off-origin",
2347
+ "expression() executes arbitrary script in legacy engines",
2348
+ "the javascript: scheme executes script",
2349
+ "the vbscript: scheme executes script",
2350
+ "-moz-binding loads and runs remote XBL",
2351
+ "behavior: loads and runs a remote HTC script",
2352
+ "@import pulls in a remote stylesheet",
2353
+ "image-set() fetches remote images, the same exfiltration channel as url()",
2354
+ "filter:progid activates legacy scriptable filters"
2355
+ ];
2356
+ const declaration = ctx?.property ? `${ctx.property}: ${value}` : value;
2357
+ const where = describeElement(ctx?.element);
2358
+ const key = `${where}|${declaration}`;
2359
+ if (warnedDeclarations.has(key)) return "";
2360
+ if (warnedDeclarations.size >= MAX_WARNED_DECLARATIONS) {
2361
+ if (declarationCapAnnounced) return "";
2362
+ declarationCapAnnounced = true;
2363
+ return `${MAX_WARNED_DECLARATIONS} distinct dropped style declarations have been reported; suppressing further ones for the rest of this session so they cannot flood the console. The declarations are still dropped \u2014 only the reporting stops. Fix the reported ones and reload to see any that remain.`;
2364
+ }
2365
+ warnedDeclarations.add(key);
2366
+ return `style declaration "${declaration}" was dropped by the style sanitizer on ${where} \u2014 ${reasons[reason]}. The element renders without it, which usually looks like a missing image rather than an error. Sanctioned alternatives: for a content image use an <img> element (or a <picture>); for decoration, put the rule in a stylesheet and set a class instead \u2014 stylesheet CSS is not sanitized, only inline style attributes are. If this declaration is not yours, it came from untrusted data and the drop just prevented an exfiltration.`;
2367
+ });
2368
+ }
2369
+ function blockedCssReason(value) {
2370
+ if (!CSS_DANGER_GATE.test(value)) return -1;
2325
2371
  const normalized = value.includes("\\") ? decodeCssEscapes2(value) : value;
2326
2372
  const lower = normalized.toLowerCase().replace(/\s+/g, "");
2327
- if (lower.includes("url(") || lower.includes("expression(") || lower.includes("javascript:") || lower.includes("vbscript:") || lower.includes("-moz-binding") || lower.includes("behavior:") || lower.includes("@import") || lower.includes("image-set(") || lower.includes("filter:progid")) {
2328
- return "";
2373
+ for (let i = 0; i < BLOCKED_CSS_NEEDLES.length; i++) {
2374
+ if (lower.includes(BLOCKED_CSS_NEEDLES[i])) return i;
2329
2375
  }
2330
- return value;
2376
+ return -1;
2331
2377
  }
2332
- function sanitizeStyleAttribute(cssText) {
2378
+ function sanitizeCSSValue(value, context) {
2379
+ const reason = blockedCssReason(value);
2380
+ if (reason === -1) return value;
2381
+ warnDroppedDeclaration(value, reason, context);
2382
+ return "";
2383
+ }
2384
+ function sanitizeStyleAttribute(cssText, context) {
2333
2385
  const input = String(cssText);
2334
2386
  if (input.trim() === "") return "";
2335
2387
  if (typeof document === "undefined") {
2336
- return sanitizeCSSValue(input) === "" ? "" : input;
2388
+ return sanitizeCSSValue(input, context) === "" ? "" : input;
2337
2389
  }
2338
2390
  const probe = document.createElement("div");
2339
2391
  try {
@@ -2345,8 +2397,12 @@ function sanitizeStyleAttribute(cssText) {
2345
2397
  for (let i = 0; i < probe.style.length; i++) {
2346
2398
  const property = probe.style[i];
2347
2399
  const value = probe.style.getPropertyValue(property);
2348
- if (sanitizeCSSValue(value) === "") continue;
2349
- if (sanitizeCSSValue(`${property}:${value}`) === "") continue;
2400
+ let reason = blockedCssReason(value);
2401
+ if (reason === -1) reason = blockedCssReason(`${property}:${value}`);
2402
+ if (reason !== -1) {
2403
+ warnDroppedDeclaration(value, reason, { property, element: context?.element });
2404
+ continue;
2405
+ }
2350
2406
  const priority = probe.style.getPropertyPriority(property);
2351
2407
  declarations.push(`${property}: ${value}${priority ? ` !${priority}` : ""}`);
2352
2408
  }
@@ -2374,16 +2430,15 @@ var HTML_CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set(["srcdoc"]);
2374
2430
  function isHtmlContentAttribute(name) {
2375
2431
  return HTML_CONTENT_ATTRIBUTES.has(name.toLowerCase());
2376
2432
  }
2377
- function sanitizeAttributeString(attr, value) {
2433
+ function sanitizeAttributeString(attr, value, context) {
2378
2434
  const lower = attr.toLowerCase();
2379
2435
  if (lower === "srcset") return sanitizeSrcset(value);
2380
2436
  if (URL_ATTRIBUTES.has(lower)) return sanitizeUrl(value);
2381
- if (lower === "style") return sanitizeStyleAttribute(value);
2437
+ if (lower === "style") return sanitizeStyleAttribute(value, context);
2382
2438
  return value;
2383
2439
  }
2384
2440
 
2385
2441
  // src/utils/setSafeAttribute.ts
2386
- var _isDev5 = isDev();
2387
2442
  var HTML_NS = "http://www.w3.org/1999/xhtml";
2388
2443
  var SVG_NS = "http://www.w3.org/2000/svg";
2389
2444
  var XLINK_NS = "http://www.w3.org/1999/xlink";
@@ -2406,7 +2461,7 @@ function setSafeAttribute(el, name, value, options = {}) {
2406
2461
  const ns = namespaceFor(el, name);
2407
2462
  const localName = ns ? name.slice(name.indexOf(":") + 1) : name;
2408
2463
  if (isEventHandlerAttr(name)) {
2409
- if (_isDev5) {
2464
+ if (DEV) {
2410
2465
  devWarn(
2411
2466
  `${options.label ?? "setSafeAttribute"}: refusing to set event-handler attribute "${name}". Its value would be evaluated as JavaScript. Use on:{ ${name.slice(2)}: fn } instead.`
2412
2467
  );
@@ -2421,7 +2476,7 @@ function setSafeAttribute(el, name, value, options = {}) {
2421
2476
  return true;
2422
2477
  }
2423
2478
  if (isHtmlContentAttribute(name)) {
2424
- if (_isDev5) {
2479
+ if (DEV) {
2425
2480
  devWarn(
2426
2481
  `${options.label ?? "setSafeAttribute"}: refusing to set "${name}". The browser parses this attribute as a nested HTML document, so a generic string value cannot be made safe \u2014 attribute escaping is undone before the parse. Build the frame's content another way.`
2427
2482
  );
@@ -2450,7 +2505,7 @@ function setSafeAttribute(el, name, value, options = {}) {
2450
2505
  setProp(el, idlName, idlName === "checked" ? Boolean(value) : str);
2451
2506
  return true;
2452
2507
  }
2453
- const safe = sanitizeAttributeString(name, str);
2508
+ const safe = sanitizeAttributeString(name, str, { element: el });
2454
2509
  const current = ns ? el.getAttributeNS(ns, localName) : el.getAttribute(name);
2455
2510
  if (current === safe) return true;
2456
2511
  if (ns) el.setAttributeNS(ns, name, safe);
@@ -3134,13 +3189,13 @@ var validators = {
3134
3189
  function validateProps(props, schema) {
3135
3190
  const result = { ...props };
3136
3191
  const errors = [];
3137
- const isDev2 = typeof process === "undefined" || process?.env?.NODE_ENV !== "production";
3192
+ const isDev = typeof process === "undefined" || process?.env?.NODE_ENV !== "production";
3138
3193
  for (const [key, def] of Object.entries(schema)) {
3139
3194
  const propDef = typeof def === "function" ? { type: def } : def;
3140
3195
  if (result[key] == null && propDef.default !== void 0) {
3141
3196
  result[key] = typeof propDef.default === "function" ? propDef.default() : propDef.default;
3142
3197
  }
3143
- if (!isDev2) continue;
3198
+ if (!isDev) continue;
3144
3199
  if (propDef.required && result[key] == null) {
3145
3200
  errors.push(`Prop '${key}' is required`);
3146
3201
  continue;
@@ -3155,7 +3210,7 @@ function validateProps(props, schema) {
3155
3210
  if (validResult !== true) errors.push(validResult);
3156
3211
  }
3157
3212
  }
3158
- if (errors.length > 0 && isDev2) {
3213
+ if (errors.length > 0 && isDev) {
3159
3214
  console.warn(`[SibuJS] Prop validation errors:
3160
3215
  ${errors.map((e) => ` - ${e}`).join("\n")}`);
3161
3216
  }
package/dist/ui.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- export { A as AttributeSource, a as AttributeValue, B as BoundFieldProps, C as CustomElementOptions, F as FieldConfig, b as FocusTrap, c as FormConfig, d as FormField, e as FormReturn, I as IntersectionResult, M as MaskOptions, T as Toast, f as ToastInstance, V as ValidatorFn, g as VirtualList, h as VirtualListProps, _ as __resetDialogStack, i as announce, j as aria, k as bindAttrs, l as bindBoolAttr, m as bindData, n as bindField, o as creditCardMask, p as custom, q as dateMask, r as defineElement, s as dialog, t as email, u as eventBus, v as focus, w as form, x as hotkey, y as infiniteScroll, z as inputMask, D as intersection, E as lazyLoad, G as matchesPattern, H as max, J as maxLength, K as min, L as minLength, N as pagination, O as phoneMask, P as removeScopedStyle, Q as required, R as scopedStyle, S as ssnMask, U as svgElement, W as timeMask, X as toast, Y as withScopedStyle, Z as zipMask } from './customElement-CKLoO63w.cjs';
1
+ export { A as AttributeSource, a as AttributeValue, B as BoundFieldProps, C as CustomElementOptions, F as FieldConfig, b as FocusTrap, c as FormConfig, d as FormField, e as FormReturn, I as IntersectionResult, M as MaskOptions, T as Toast, f as ToastInstance, V as ValidatorFn, g as VirtualList, h as VirtualListProps, _ as __resetDialogStack, i as announce, j as aria, k as bindAttrs, l as bindBoolAttr, m as bindData, n as bindField, o as creditCardMask, p as custom, q as dateMask, r as defineElement, s as dialog, t as email, u as eventBus, v as focus, w as form, x as hotkey, y as infiniteScroll, z as inputMask, D as intersection, E as lazyLoad, G as matchesPattern, H as max, J as maxLength, K as min, L as minLength, N as pagination, O as phoneMask, P as removeScopedStyle, Q as required, R as scopedStyle, S as ssnMask, U as svgElement, W as timeMask, X as toast, Y as withScopedStyle, Z as zipMask } from './customElement-CNZxEB9G.cjs';
2
2
  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-Hg6ITAEF.cjs';
3
3
 
4
4
  interface FormActionHandle<TArgs extends unknown[], TResult> {
@@ -250,6 +250,12 @@ interface ScrollLockHandle {
250
250
  /** Release this handle's lock. Idempotent. */
251
251
  unlock: () => void;
252
252
  }
253
+ /**
254
+ * Acquire a reference-counted lock on document scrolling, so nested overlays
255
+ * can each lock and unlock without one release re-enabling scroll for all.
256
+ *
257
+ * @returns A handle with `lock`, `unlock` and `isLocked`.
258
+ */
253
259
  declare function scrollLock(): ScrollLockHandle;
254
260
 
255
261
  export { type DialogAriaHandle, type DialogAriaOptions, type FocusManagerHandle, type FocusManagerOptions, type FormActionHandle, type IntervalHandle, type ListboxHandle, type ListboxOptions, type ScrollLockHandle, type TimeoutHandle, createDialogAria, createFocusManager, createListbox, formAction, hover, interval, lazyEffect, scrollLock, timeout };
package/dist/ui.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { A as AttributeSource, a as AttributeValue, B as BoundFieldProps, C as CustomElementOptions, F as FieldConfig, b as FocusTrap, c as FormConfig, d as FormField, e as FormReturn, I as IntersectionResult, M as MaskOptions, T as Toast, f as ToastInstance, V as ValidatorFn, g as VirtualList, h as VirtualListProps, _ as __resetDialogStack, i as announce, j as aria, k as bindAttrs, l as bindBoolAttr, m as bindData, n as bindField, o as creditCardMask, p as custom, q as dateMask, r as defineElement, s as dialog, t as email, u as eventBus, v as focus, w as form, x as hotkey, y as infiniteScroll, z as inputMask, D as intersection, E as lazyLoad, G as matchesPattern, H as max, J as maxLength, K as min, L as minLength, N as pagination, O as phoneMask, P as removeScopedStyle, Q as required, R as scopedStyle, S as ssnMask, U as svgElement, W as timeMask, X as toast, Y as withScopedStyle, Z as zipMask } from './customElement-CKLoO63w.js';
1
+ export { A as AttributeSource, a as AttributeValue, B as BoundFieldProps, C as CustomElementOptions, F as FieldConfig, b as FocusTrap, c as FormConfig, d as FormField, e as FormReturn, I as IntersectionResult, M as MaskOptions, T as Toast, f as ToastInstance, V as ValidatorFn, g as VirtualList, h as VirtualListProps, _ as __resetDialogStack, i as announce, j as aria, k as bindAttrs, l as bindBoolAttr, m as bindData, n as bindField, o as creditCardMask, p as custom, q as dateMask, r as defineElement, s as dialog, t as email, u as eventBus, v as focus, w as form, x as hotkey, y as infiniteScroll, z as inputMask, D as intersection, E as lazyLoad, G as matchesPattern, H as max, J as maxLength, K as min, L as minLength, N as pagination, O as phoneMask, P as removeScopedStyle, Q as required, R as scopedStyle, S as ssnMask, U as svgElement, W as timeMask, X as toast, Y as withScopedStyle, Z as zipMask } from './customElement-CNZxEB9G.js';
2
2
  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-Hg6ITAEF.js';
3
3
 
4
4
  interface FormActionHandle<TArgs extends unknown[], TResult> {
@@ -250,6 +250,12 @@ interface ScrollLockHandle {
250
250
  /** Release this handle's lock. Idempotent. */
251
251
  unlock: () => void;
252
252
  }
253
+ /**
254
+ * Acquire a reference-counted lock on document scrolling, so nested overlays
255
+ * can each lock and unlock without one release re-enabling scroll for all.
256
+ *
257
+ * @returns A handle with `lock`, `unlock` and `isLocked`.
258
+ */
253
259
  declare function scrollLock(): ScrollLockHandle;
254
260
 
255
261
  export { type DialogAriaHandle, type DialogAriaOptions, type FocusManagerHandle, type FocusManagerOptions, type FormActionHandle, type IntervalHandle, type ListboxHandle, type ListboxOptions, type ScrollLockHandle, type TimeoutHandle, createDialogAria, createFocusManager, createListbox, formAction, hover, interval, lazyEffect, scrollLock, timeout };
package/dist/ui.js CHANGED
@@ -38,7 +38,7 @@ import {
38
38
  toast,
39
39
  withScopedStyle,
40
40
  zipMask
41
- } from "./chunk-XIHMS2Y6.js";
41
+ } from "./chunk-SF3TB4K6.js";
42
42
  import {
43
43
  RenderProp,
44
44
  assertType,
@@ -58,24 +58,24 @@ import {
58
58
  import {
59
59
  createId
60
60
  } from "./chunk-CCSJMTRN.js";
61
- import "./chunk-SHDVWATN.js";
62
- import "./chunk-DCDS2PQ6.js";
63
- import "./chunk-5INI7D2L.js";
61
+ import "./chunk-HJEPKZ2O.js";
62
+ import "./chunk-2LOYNGBY.js";
63
+ import "./chunk-IXKSNWV5.js";
64
64
  import {
65
65
  globalSingleton
66
66
  } from "./chunk-7ZHH77QA.js";
67
67
  import {
68
68
  effect
69
- } from "./chunk-PFTDGEPZ.js";
70
- import "./chunk-TIRZCERI.js";
69
+ } from "./chunk-7VEC3UD2.js";
70
+ import "./chunk-5DXA2J44.js";
71
71
  import {
72
72
  registerDisposer
73
- } from "./chunk-DHDZ7IVN.js";
73
+ } from "./chunk-QKRPLZ2V.js";
74
74
  import {
75
75
  signal
76
- } from "./chunk-UFSQEO7P.js";
77
- import "./chunk-D3SZ4LVS.js";
78
- import "./chunk-VPP2FONR.js";
76
+ } from "./chunk-HECVW343.js";
77
+ import "./chunk-7ALC7KGE.js";
78
+ import "./chunk-2WLZ6757.js";
79
79
 
80
80
  // src/ui/formAction.ts
81
81
  function formAction(fn) {
package/dist/widgets.cjs CHANGED
@@ -41,23 +41,17 @@ function createId(prefix = "sibu") {
41
41
  }
42
42
 
43
43
  // src/core/dev.ts
44
- function isDev() {
45
- return typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
46
- // The bare `__SIBU_DEV__` is a bundler define that only exists in
47
- // production builds; under the test runner it is always undefined, so
48
- // this branch is unreachable here.
49
- /* v8 ignore next 2 */
50
- typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
51
- );
52
- }
53
- var _isDev = isDev();
44
+ var DEV = typeof __SIBU_DEV__ !== "undefined" ? !!__SIBU_DEV__ : typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
45
+ // safe default: off in browser, on in test/dev Node
46
+ typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
47
+ );
54
48
  function devAssert(condition, message) {
55
- if (_isDev && !condition) {
49
+ if ((typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) && !condition) {
56
50
  throw new Error(`[SibuJS] ${message}`);
57
51
  }
58
52
  }
59
53
  function devWarn(message) {
60
- if (_isDev) {
54
+ if (typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) {
61
55
  console.warn(`[SibuJS] ${message}`);
62
56
  }
63
57
  }
@@ -606,17 +600,16 @@ function forEachSubscriber(signal2, visit) {
606
600
  }
607
601
 
608
602
  // src/reactivity/track.ts
609
- var _isDev2 = isDev();
610
- var _runtimeVersion = true ? "4.1.0" : "dev";
603
+ var _runtimeVersion = true ? "4.2.0" : "dev";
611
604
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
612
605
  function resolveReactiveApi() {
613
606
  const g = globalThis;
614
607
  const existing = g[REGISTRY_KEY];
615
608
  if (existing) {
616
- if (_isDev2 && !existing.__dupWarned) {
609
+ if (DEV && !existing.__dupWarned) {
617
610
  existing.__dupWarned = true;
618
611
  devWarn(
619
- `Multiple instances of the reactive runtime detected on this page (active: ${existing.version}, duplicate: ${_runtimeVersion}). Reactivity still works \u2014 all copies share the first one \u2014 but de-duplicate sibujs in your bundler (e.g. Vite optimizeDeps.exclude: ['sibujs'] or resolve.dedupe: ['sibujs']).`
612
+ `Multiple instances of the reactive runtime detected on this page (active: ${existing.version}, duplicate: ${_runtimeVersion}). Reactivity is NOT broken and no bundler configuration is required: every copy routes through the first one via a global registry, so signal writes still reach subscribers registered by any copy. This is only a SIZE issue \u2014 the duplicate copy's bytes are downloaded and parsed for nothing. If you want them back, de-duplicate sibujs in your bundler (Vite: resolve.dedupe: ['sibujs']). Mismatched VERSIONS above are worth fixing regardless, since only the first one loaded is used.`
620
613
  );
621
614
  }
622
615
  return existing;
@@ -951,7 +944,6 @@ var isBatching = API2.isBatching;
951
944
 
952
945
  // src/core/signals/signal.ts
953
946
  var _g2 = globalThis;
954
- var _isDev3 = isDev();
955
947
  function signal(initial, options) {
956
948
  const state2 = {
957
949
  value: initial,
@@ -964,7 +956,7 @@ function signal(initial, options) {
964
956
  _d: false,
965
957
  _validate: void 0
966
958
  };
967
- const debugName = _isDev3 ? options?.name : void 0;
959
+ const debugName = DEV ? options?.name : void 0;
968
960
  const equalsFn = options?.equals;
969
961
  if (debugName) state2.__name = debugName;
970
962
  function get() {
@@ -981,7 +973,7 @@ function signal(initial, options) {
981
973
  if (equalsFn(prev, newValue)) return;
982
974
  state2.value = newValue;
983
975
  state2.__v++;
984
- if (_isDev3) {
976
+ if (DEV) {
985
977
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
986
978
  if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
987
979
  }
@@ -989,7 +981,7 @@ function signal(initial, options) {
989
981
  notifySubscribers2(state2);
990
982
  }
991
983
  };
992
- } else if (_isDev3) {
984
+ } else if (DEV) {
993
985
  set = (next) => {
994
986
  const prev = state2.value;
995
987
  const newValue = typeof next === "function" ? next(prev) : next;
@@ -1014,7 +1006,7 @@ function signal(initial, options) {
1014
1006
  }
1015
1007
  };
1016
1008
  }
1017
- if (_isDev3) {
1009
+ if (DEV) {
1018
1010
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1019
1011
  if (hook) hook.emit("signal:create", { signal: state2, name: debugName, getter: get, initial });
1020
1012
  }
@@ -119,6 +119,14 @@ interface SelectOptions<T> {
119
119
  * by `highlightNext`/`highlightPrev`/typeahead and rejected by `select`. */
120
120
  isDisabled?: (item: T) => boolean;
121
121
  }
122
+ /**
123
+ * Selection state for a list — single or multiple, with keyboard-friendly
124
+ * helpers. Headless: it owns state, not markup.
125
+ *
126
+ * @param options Item list, selection mode, and comparison strategy.
127
+ * @returns A handle exposing `selectedItem`/`selectedItems` accessors and the
128
+ * `select`, `deselect`, `toggle` and `clear` operations.
129
+ */
122
130
  declare function select<T>(options: SelectOptions<T>): {
123
131
  selectedItems: () => T[];
124
132
  selectedItem: () => T | null;
package/dist/widgets.d.ts CHANGED
@@ -119,6 +119,14 @@ interface SelectOptions<T> {
119
119
  * by `highlightNext`/`highlightPrev`/typeahead and rejected by `select`. */
120
120
  isDisabled?: (item: T) => boolean;
121
121
  }
122
+ /**
123
+ * Selection state for a list — single or multiple, with keyboard-friendly
124
+ * helpers. Headless: it owns state, not markup.
125
+ *
126
+ * @param options Item list, selection mode, and comparison strategy.
127
+ * @returns A handle exposing `selectedItem`/`selectedItems` accessors and the
128
+ * `select`, `deselect`, `toggle` and `clear` operations.
129
+ */
122
130
  declare function select<T>(options: SelectOptions<T>): {
123
131
  selectedItems: () => T[];
124
132
  selectedItem: () => T | null;
package/dist/widgets.js CHANGED
@@ -8,16 +8,16 @@ import {
8
8
  select,
9
9
  tabs,
10
10
  tooltip
11
- } from "./chunk-FMHIQNMP.js";
12
- import "./chunk-KSOEJGFO.js";
11
+ } from "./chunk-QCFLDD7C.js";
12
+ import "./chunk-BMYYZWHX.js";
13
13
  import "./chunk-CCSJMTRN.js";
14
- import "./chunk-DCDS2PQ6.js";
15
- import "./chunk-5INI7D2L.js";
16
- import "./chunk-PFTDGEPZ.js";
17
- import "./chunk-TIRZCERI.js";
18
- import "./chunk-UFSQEO7P.js";
19
- import "./chunk-D3SZ4LVS.js";
20
- import "./chunk-VPP2FONR.js";
14
+ import "./chunk-2LOYNGBY.js";
15
+ import "./chunk-IXKSNWV5.js";
16
+ import "./chunk-7VEC3UD2.js";
17
+ import "./chunk-5DXA2J44.js";
18
+ import "./chunk-HECVW343.js";
19
+ import "./chunk-7ALC7KGE.js";
20
+ import "./chunk-2WLZ6757.js";
21
21
  export {
22
22
  accordion,
23
23
  combobox,