sibujs 1.2.0 → 1.4.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 (95) hide show
  1. package/README.md +29 -25
  2. package/dist/browser.cjs +804 -2
  3. package/dist/browser.d.cts +591 -1
  4. package/dist/browser.d.ts +591 -1
  5. package/dist/browser.js +50 -8
  6. package/dist/build.cjs +655 -237
  7. package/dist/build.js +15 -93
  8. package/dist/cdn.global.js +188 -7
  9. package/dist/chunk-2BYQDGN3.js +742 -0
  10. package/dist/chunk-32DY64NT.js +282 -0
  11. package/dist/chunk-3AIRKM3B.js +1263 -0
  12. package/dist/chunk-3X2YG6YM.js +505 -0
  13. package/dist/chunk-5X6PP2UK.js +28 -0
  14. package/dist/chunk-77L6NL3X.js +1097 -0
  15. package/dist/chunk-BGN5ZMP4.js +26 -0
  16. package/dist/chunk-BTU3TJDS.js +365 -0
  17. package/dist/chunk-CHF5OHIA.js +61 -0
  18. package/dist/chunk-CMBFNA7L.js +27 -0
  19. package/dist/chunk-CNZ35WI2.js +178 -0
  20. package/dist/chunk-DAHRH4ON.js +331 -0
  21. package/dist/chunk-EBGIRKQY.js +616 -0
  22. package/dist/chunk-EUZND3CB.js +27 -0
  23. package/dist/chunk-F3FA4F32.js +292 -0
  24. package/dist/chunk-JAKHTMQU.js +1000 -0
  25. package/dist/chunk-JCI5M6U6.js +956 -0
  26. package/dist/chunk-KQPDEVVS.js +398 -0
  27. package/dist/chunk-M4NLBH4I.js +725 -0
  28. package/dist/chunk-NEKUBFPT.js +60 -0
  29. package/dist/chunk-NYVAC6P5.js +37 -0
  30. package/dist/chunk-PTQJDMRT.js +146 -0
  31. package/dist/chunk-QWZG56ET.js +2744 -0
  32. package/dist/chunk-TSOKIX5Z.js +654 -0
  33. package/dist/chunk-UHNL42EF.js +2730 -0
  34. package/dist/chunk-VRW3FULF.js +725 -0
  35. package/dist/chunk-WZSPOOER.js +84 -0
  36. package/dist/chunk-YT6HQ6AM.js +14 -0
  37. package/dist/chunk-ZD6OAMTH.js +277 -0
  38. package/dist/chunk-ZWKZCBO6.js +317 -0
  39. package/dist/contracts-DDrwxvJ-.d.cts +245 -0
  40. package/dist/contracts-DDrwxvJ-.d.ts +245 -0
  41. package/dist/contracts-xo5ckdRP.d.cts +240 -0
  42. package/dist/contracts-xo5ckdRP.d.ts +240 -0
  43. package/dist/data.cjs +35 -2
  44. package/dist/data.d.cts +7 -0
  45. package/dist/data.d.ts +7 -0
  46. package/dist/data.js +9 -8
  47. package/dist/devtools.cjs +122 -0
  48. package/dist/devtools.d.cts +69 -461
  49. package/dist/devtools.d.ts +69 -461
  50. package/dist/devtools.js +127 -6
  51. package/dist/ecosystem.cjs +23 -6
  52. package/dist/ecosystem.d.cts +1 -1
  53. package/dist/ecosystem.d.ts +1 -1
  54. package/dist/ecosystem.js +10 -9
  55. package/dist/extras.cjs +1208 -88
  56. package/dist/extras.d.cts +6 -6
  57. package/dist/extras.d.ts +6 -6
  58. package/dist/extras.js +70 -33
  59. package/dist/index.cjs +663 -158
  60. package/dist/index.d.cts +398 -40
  61. package/dist/index.d.ts +398 -40
  62. package/dist/index.js +39 -21
  63. package/dist/introspect-BumjnBKr.d.cts +477 -0
  64. package/dist/introspect-CZrlcaYy.d.ts +477 -0
  65. package/dist/introspect-Cb0zgpi2.d.cts +477 -0
  66. package/dist/introspect-Y2xNXGSf.d.ts +477 -0
  67. package/dist/motion.js +4 -4
  68. package/dist/patterns.cjs +51 -24
  69. package/dist/patterns.d.cts +19 -57
  70. package/dist/patterns.d.ts +19 -57
  71. package/dist/patterns.js +8 -16
  72. package/dist/performance.js +4 -4
  73. package/dist/plugins.cjs +429 -82
  74. package/dist/plugins.d.cts +27 -4
  75. package/dist/plugins.d.ts +27 -4
  76. package/dist/plugins.js +156 -37
  77. package/dist/ssr-4PBXAOO3.js +40 -0
  78. package/dist/ssr-Do_SiVoL.d.cts +201 -0
  79. package/dist/ssr-Do_SiVoL.d.ts +201 -0
  80. package/dist/ssr.cjs +312 -60
  81. package/dist/ssr.d.cts +10 -1
  82. package/dist/ssr.d.ts +10 -1
  83. package/dist/ssr.js +13 -10
  84. package/dist/tagFactory-DaJ0YWX6.d.cts +47 -0
  85. package/dist/tagFactory-DaJ0YWX6.d.ts +47 -0
  86. package/dist/testing.cjs +233 -2
  87. package/dist/testing.d.cts +42 -1
  88. package/dist/testing.d.ts +42 -1
  89. package/dist/testing.js +129 -2
  90. package/dist/ui.cjs +374 -8
  91. package/dist/ui.d.cts +252 -2
  92. package/dist/ui.d.ts +252 -2
  93. package/dist/ui.js +329 -11
  94. package/dist/widgets.js +7 -7
  95. package/package.json +1 -1
package/dist/extras.cjs CHANGED
@@ -32,6 +32,7 @@ __export(extras_exports, {
32
32
  VirtualList: () => VirtualList,
33
33
  accordion: () => accordion,
34
34
  animate: () => animate,
35
+ animationFrame: () => animationFrame,
35
36
  announce: () => announce,
36
37
  antdAdapter: () => antdAdapter,
37
38
  aria: () => aria,
@@ -44,6 +45,8 @@ __export(extras_exports, {
44
45
  block: () => block,
45
46
  bounceIn: () => bounceIn,
46
47
  bounceOut: () => bounceOut,
48
+ bounds: () => bounds,
49
+ broadcast: () => broadcast,
47
50
  bundlerMetadata: () => bundlerMetadata,
48
51
  calculateDelay: () => calculateDelay,
49
52
  chakraAdapter: () => chakraAdapter,
@@ -61,7 +64,6 @@ __export(extras_exports, {
61
64
  combobox: () => combobox,
62
65
  compareSemVer: () => compareSemVer,
63
66
  componentAdapter: () => componentAdapter,
64
- composable: () => composable,
65
67
  compose: () => compose,
66
68
  composeMiddleware: () => composeMiddleware,
67
69
  conditional: () => conditional,
@@ -71,12 +73,10 @@ __export(extras_exports, {
71
73
  createBundle: () => createBundle,
72
74
  createChunkRegistry: () => createChunkRegistry,
73
75
  createDevtoolsOverlay: () => createDevtoolsOverlay,
74
- createEffect: () => createEffect,
75
76
  createErrorReporter: () => createErrorReporter,
76
77
  createGuard: () => createGuard,
77
78
  createHMRBoundary: () => createHMRBoundary,
78
79
  createISR: () => createISR,
79
- createMemo: () => createMemo,
80
80
  createMicroApp: () => createMicroApp,
81
81
  createMiddlewareChain: () => createMiddlewareChain,
82
82
  createMigrationRunner: () => createMigrationRunner,
@@ -85,7 +85,6 @@ __export(extras_exports, {
85
85
  createProfiler: () => createProfiler,
86
86
  createSSRCache: () => createSSRCache,
87
87
  createSharedScope: () => createSharedScope,
88
- createSignal: () => createSignal,
89
88
  createSlots: () => createSlots,
90
89
  createTestHarness: () => createTestHarness,
91
90
  createTheme: () => createTheme,
@@ -117,10 +116,12 @@ __export(extras_exports, {
117
116
  email: () => email,
118
117
  enableDebug: () => enableDebug,
119
118
  env: () => env,
119
+ escapeScriptJson: () => escapeScriptJson,
120
120
  eventBus: () => eventBus,
121
121
  executeLoader: () => executeLoader,
122
122
  fadeIn: () => fadeIn,
123
123
  fadeOut: () => fadeOut,
124
+ favicon: () => favicon,
124
125
  fileUpload: () => fileUpload,
125
126
  flipIn: () => flipIn,
126
127
  flushScheduler: () => flushScheduler,
@@ -129,6 +130,8 @@ __export(extras_exports, {
129
130
  formatCurrency: () => formatCurrency,
130
131
  formatError: () => formatError,
131
132
  formatNumber: () => formatNumber,
133
+ fullscreen: () => fullscreen,
134
+ gamepad: () => gamepad,
132
135
  generateStaticSite: () => generateStaticSite,
133
136
  geo: () => geo,
134
137
  getActiveDevTools: () => getActiveDevTools,
@@ -147,6 +150,7 @@ __export(extras_exports, {
147
150
  hydrateIslands: () => hydrateIslands,
148
151
  hydrateProgressively: () => hydrateProgressively,
149
152
  idle: () => idle,
153
+ imageLoader: () => imageLoader,
150
154
  infiniteQuery: () => infiniteQuery,
151
155
  infiniteScroll: () => infiniteScroll,
152
156
  initDevTools: () => initDevTools,
@@ -159,6 +163,7 @@ __export(extras_exports, {
159
163
  isHMRAvailable: () => isHMRAvailable,
160
164
  isWasmCached: () => isWasmCached,
161
165
  island: () => island,
166
+ keyboard: () => keyboard,
162
167
  lazyChunk: () => lazyChunk,
163
168
  lazyLoad: () => lazyLoad,
164
169
  lazyModule: () => lazyModule,
@@ -175,7 +180,10 @@ __export(extras_exports, {
175
180
  min: () => min,
176
181
  minLength: () => minLength,
177
182
  mobXAdapter: () => mobXAdapter,
183
+ mouse: () => mouse,
178
184
  mutation: () => mutation,
185
+ mutationObserver: () => mutationObserver,
186
+ network: () => network,
179
187
  noSideEffect: () => noSideEffect,
180
188
  normalize: () => normalize,
181
189
  normalizedStore: () => normalizedStore,
@@ -192,6 +200,7 @@ __export(extras_exports, {
192
200
  persisted: () => persisted,
193
201
  phoneMask: () => phoneMask,
194
202
  plugin: () => plugin,
203
+ pointerLock: () => pointerLock,
195
204
  popover: () => popover,
196
205
  precompile: () => precompile,
197
206
  prefetch: () => prefetch,
@@ -243,6 +252,7 @@ __export(extras_exports, {
243
252
  slideIn: () => slideIn,
244
253
  slideOut: () => slideOut,
245
254
  socket: () => socket,
255
+ speech: () => speech,
246
256
  spring: () => spring,
247
257
  springSignal: () => springSignal,
248
258
  ssnMask: () => ssnMask,
@@ -254,8 +264,11 @@ __export(extras_exports, {
254
264
  stream: () => stream,
255
265
  suspenseSwapScript: () => suspenseSwapScript,
256
266
  svgElement: () => svgElement,
267
+ svgFavicon: () => svgFavicon,
268
+ swipe: () => swipe,
257
269
  syncAdapter: () => syncAdapter,
258
270
  tabs: () => tabs,
271
+ textSelection: () => textSelection,
259
272
  throttle: () => throttle,
260
273
  timeMask: () => timeMask,
261
274
  timeline: () => timeline,
@@ -270,11 +283,16 @@ __export(extras_exports, {
270
283
  triggerPluginUnmount: () => triggerPluginUnmount,
271
284
  trustHTML: () => trustHTML,
272
285
  uniqueId: () => uniqueId,
286
+ urlState: () => urlState,
273
287
  validateProps: () => validateProps,
274
288
  validators: () => validators,
289
+ vibrate: () => vibrate,
275
290
  viewTransition: () => viewTransition,
291
+ visibility: () => visibility,
292
+ wakeLock: () => wakeLock,
276
293
  walkDependencyGraph: () => walkDependencyGraph,
277
294
  wasm: () => wasm,
295
+ windowSize: () => windowSize,
278
296
  withBoundary: () => withBoundary,
279
297
  withDefaults: () => withDefaults,
280
298
  withErrorTracking: () => withErrorTracking,
@@ -1512,6 +1530,13 @@ async function preloadRoute(route, context2) {
1512
1530
  }
1513
1531
 
1514
1532
  // src/ui/socket.ts
1533
+ function validateWsUrl(raw) {
1534
+ const trimmed = raw.replace(/[\x00-\x20\x7f-\x9f]+/g, "").trim();
1535
+ if (!trimmed) return null;
1536
+ const lower = trimmed.toLowerCase();
1537
+ if (lower.startsWith("ws://") || lower.startsWith("wss://")) return trimmed;
1538
+ return null;
1539
+ }
1515
1540
  function socket(url, options) {
1516
1541
  const autoReconnect = options?.autoReconnect ?? false;
1517
1542
  const reconnectDelay = options?.reconnectDelay ?? 1e3;
@@ -1530,8 +1555,13 @@ function socket(url, options) {
1530
1555
  }
1531
1556
  function connect() {
1532
1557
  if (disposed) return;
1558
+ const safeUrl = validateWsUrl(getUrl());
1559
+ if (safeUrl === null) {
1560
+ setStatus("closed");
1561
+ return;
1562
+ }
1533
1563
  setStatus("connecting");
1534
- ws = new WebSocket(getUrl(), protocols);
1564
+ ws = new WebSocket(safeUrl, protocols);
1535
1565
  ws.onopen = () => {
1536
1566
  setStatus("open");
1537
1567
  reconnectCount = 0;
@@ -1592,7 +1622,34 @@ function socket(url, options) {
1592
1622
  return { data: data2, status, send, close, dispose };
1593
1623
  }
1594
1624
 
1625
+ // src/utils/sanitize.ts
1626
+ function sanitizeUrl(url) {
1627
+ const trimmed = url.replace(/[\x00-\x20\x7f-\x9f]+/g, "").trim();
1628
+ if (!trimmed) return "";
1629
+ const lower = trimmed.toLowerCase();
1630
+ if (lower.startsWith("javascript:") || lower.startsWith("data:") || lower.startsWith("vbscript:") || lower.startsWith("blob:")) {
1631
+ return "";
1632
+ }
1633
+ return trimmed;
1634
+ }
1635
+ function sanitizeCSSValue(value) {
1636
+ const lower = value.toLowerCase().replace(/\s+/g, "");
1637
+ if (lower.includes("url(") || lower.includes("expression(") || lower.includes("javascript:") || lower.includes("-moz-binding")) {
1638
+ return "";
1639
+ }
1640
+ return value;
1641
+ }
1642
+ var URL_ATTRIBUTES = /* @__PURE__ */ new Set(["href", "src", "action", "formaction", "cite", "poster", "background", "srcset"]);
1643
+ function isUrlAttribute(attr) {
1644
+ return URL_ATTRIBUTES.has(attr);
1645
+ }
1646
+
1595
1647
  // src/ui/stream.ts
1648
+ function validateSseUrl(raw) {
1649
+ const safe = sanitizeUrl(raw);
1650
+ if (!safe) return null;
1651
+ return safe;
1652
+ }
1596
1653
  function stream(url, options) {
1597
1654
  const autoReconnect = options?.autoReconnect ?? false;
1598
1655
  const [data2, setData] = signal(null);
@@ -1603,8 +1660,13 @@ function stream(url, options) {
1603
1660
  let reconnectTimer = null;
1604
1661
  function connect() {
1605
1662
  if (disposed) return;
1663
+ const safeUrl = validateSseUrl(url);
1664
+ if (safeUrl === null) {
1665
+ setStatus("closed");
1666
+ return;
1667
+ }
1606
1668
  setStatus("connecting");
1607
- source2 = new EventSource(url, {
1669
+ source2 = new EventSource(safeUrl, {
1608
1670
  withCredentials: options?.withCredentials ?? false
1609
1671
  });
1610
1672
  source2.onopen = () => {
@@ -2103,6 +2165,766 @@ function formatCurrency(value, currency, options) {
2103
2165
  }).format(value);
2104
2166
  }
2105
2167
 
2168
+ // src/browser/visibility.ts
2169
+ function visibility() {
2170
+ if (typeof document === "undefined") {
2171
+ const [visible2] = signal(true);
2172
+ return { visible: visible2, dispose: () => {
2173
+ } };
2174
+ }
2175
+ const [visible, setVisible] = signal(!document.hidden);
2176
+ const handler = () => setVisible(!document.hidden);
2177
+ document.addEventListener("visibilitychange", handler);
2178
+ function dispose() {
2179
+ document.removeEventListener("visibilitychange", handler);
2180
+ }
2181
+ return { visible, dispose };
2182
+ }
2183
+
2184
+ // src/browser/network.ts
2185
+ function network() {
2186
+ const connection = typeof navigator !== "undefined" ? navigator.connection ?? navigator.mozConnection ?? navigator.webkitConnection : void 0;
2187
+ const [effectiveType, setEffectiveType] = signal(connection?.effectiveType ?? "unknown");
2188
+ const [downlink, setDownlink] = signal(connection?.downlink ?? 0);
2189
+ const [rtt, setRtt] = signal(connection?.rtt ?? 0);
2190
+ const [saveData, setSaveData] = signal(connection?.saveData ?? false);
2191
+ if (!connection) {
2192
+ return { effectiveType, downlink, rtt, saveData, dispose: () => {
2193
+ } };
2194
+ }
2195
+ const update = () => {
2196
+ setEffectiveType(connection.effectiveType ?? "unknown");
2197
+ setDownlink(connection.downlink ?? 0);
2198
+ setRtt(connection.rtt ?? 0);
2199
+ setSaveData(connection.saveData ?? false);
2200
+ };
2201
+ connection.addEventListener("change", update);
2202
+ function dispose() {
2203
+ connection?.removeEventListener("change", update);
2204
+ }
2205
+ return { effectiveType, downlink, rtt, saveData, dispose };
2206
+ }
2207
+
2208
+ // src/browser/mouse.ts
2209
+ function mouse(options = {}) {
2210
+ const [x, setX] = signal(0);
2211
+ const [y, setY] = signal(0);
2212
+ if (typeof window === "undefined") {
2213
+ return { x, y, dispose: () => {
2214
+ } };
2215
+ }
2216
+ const target = options.target ?? window;
2217
+ const trackTouch = options.touch ?? true;
2218
+ const onMove = (e) => {
2219
+ setX(e.clientX);
2220
+ setY(e.clientY);
2221
+ };
2222
+ const onTouchMove = (e) => {
2223
+ if (e.touches.length === 0) return;
2224
+ setX(e.touches[0].clientX);
2225
+ setY(e.touches[0].clientY);
2226
+ };
2227
+ target.addEventListener("mousemove", onMove, { passive: true });
2228
+ if (trackTouch) {
2229
+ target.addEventListener("touchmove", onTouchMove, { passive: true });
2230
+ }
2231
+ function dispose() {
2232
+ target.removeEventListener("mousemove", onMove);
2233
+ if (trackTouch) {
2234
+ target.removeEventListener("touchmove", onTouchMove);
2235
+ }
2236
+ }
2237
+ return { x, y, dispose };
2238
+ }
2239
+
2240
+ // src/browser/swipe.ts
2241
+ function swipe(target, options = {}) {
2242
+ const threshold = options.threshold ?? 50;
2243
+ const [direction, setDirection] = signal(null);
2244
+ if (typeof window === "undefined") {
2245
+ return { direction, dispose: () => {
2246
+ } };
2247
+ }
2248
+ let startX = 0;
2249
+ let startY = 0;
2250
+ let tracking = false;
2251
+ const onStart = (e) => {
2252
+ if (e.touches.length === 0) return;
2253
+ startX = e.touches[0].clientX;
2254
+ startY = e.touches[0].clientY;
2255
+ tracking = true;
2256
+ };
2257
+ const onEnd = (e) => {
2258
+ if (!tracking) return;
2259
+ tracking = false;
2260
+ const touch = e.changedTouches[0];
2261
+ if (!touch) return;
2262
+ const dx = touch.clientX - startX;
2263
+ const dy = touch.clientY - startY;
2264
+ const absX = Math.abs(dx);
2265
+ const absY = Math.abs(dy);
2266
+ if (Math.max(absX, absY) < threshold) return;
2267
+ let dir;
2268
+ if (absX > absY) {
2269
+ dir = dx > 0 ? "right" : "left";
2270
+ } else {
2271
+ dir = dy > 0 ? "down" : "up";
2272
+ }
2273
+ setDirection(dir);
2274
+ options.onSwipe?.(dir, Math.max(absX, absY));
2275
+ };
2276
+ target.addEventListener("touchstart", onStart, { passive: true });
2277
+ target.addEventListener("touchend", onEnd, { passive: true });
2278
+ function dispose() {
2279
+ target.removeEventListener("touchstart", onStart);
2280
+ target.removeEventListener("touchend", onEnd);
2281
+ }
2282
+ return { direction, dispose };
2283
+ }
2284
+
2285
+ // src/browser/windowSize.ts
2286
+ function windowSize() {
2287
+ if (typeof window === "undefined") {
2288
+ const [width2] = signal(0);
2289
+ const [height2] = signal(0);
2290
+ return { width: width2, height: height2, dispose: () => {
2291
+ } };
2292
+ }
2293
+ const [width, setWidth] = signal(window.innerWidth);
2294
+ const [height, setHeight] = signal(window.innerHeight);
2295
+ const handler = () => {
2296
+ setWidth(window.innerWidth);
2297
+ setHeight(window.innerHeight);
2298
+ };
2299
+ window.addEventListener("resize", handler, { passive: true });
2300
+ function dispose() {
2301
+ window.removeEventListener("resize", handler);
2302
+ }
2303
+ return { width, height, dispose };
2304
+ }
2305
+
2306
+ // src/browser/urlState.ts
2307
+ function urlState() {
2308
+ if (typeof window === "undefined") {
2309
+ const [params2] = signal(new URLSearchParams());
2310
+ const [hash2] = signal("");
2311
+ return {
2312
+ params: params2,
2313
+ hash: hash2,
2314
+ setParams: () => {
2315
+ },
2316
+ setHash: () => {
2317
+ },
2318
+ dispose: () => {
2319
+ }
2320
+ };
2321
+ }
2322
+ const [params, setParamsSignal] = signal(new URLSearchParams(window.location.search));
2323
+ const [hash, setHashSignal] = signal(window.location.hash);
2324
+ const syncFromLocation = () => {
2325
+ setParamsSignal(new URLSearchParams(window.location.search));
2326
+ setHashSignal(window.location.hash);
2327
+ };
2328
+ const onPopState = () => syncFromLocation();
2329
+ window.addEventListener("popstate", onPopState);
2330
+ function setParams(next, opts = {}) {
2331
+ const p2 = next instanceof URLSearchParams ? next : new URLSearchParams(next);
2332
+ const query2 = p2.toString();
2333
+ const newUrl = `${window.location.pathname}${query2 ? `?${query2}` : ""}${window.location.hash}`;
2334
+ if (opts.replace) window.history.replaceState(null, "", newUrl);
2335
+ else window.history.pushState(null, "", newUrl);
2336
+ setParamsSignal(new URLSearchParams(p2));
2337
+ }
2338
+ function setHash(next, opts = {}) {
2339
+ const normalized = next.startsWith("#") ? next : next ? `#${next}` : "";
2340
+ const newUrl = `${window.location.pathname}${window.location.search}${normalized}`;
2341
+ if (opts.replace) window.history.replaceState(null, "", newUrl);
2342
+ else window.history.pushState(null, "", newUrl);
2343
+ setHashSignal(normalized);
2344
+ }
2345
+ function dispose() {
2346
+ window.removeEventListener("popstate", onPopState);
2347
+ }
2348
+ return { params, hash, setParams, setHash, dispose };
2349
+ }
2350
+
2351
+ // src/browser/broadcast.ts
2352
+ function broadcast(channelName) {
2353
+ if (typeof BroadcastChannel === "undefined") {
2354
+ const [last2] = signal(null);
2355
+ return { last: last2, post: () => {
2356
+ }, dispose: () => {
2357
+ } };
2358
+ }
2359
+ const [last, setLast] = signal(null);
2360
+ const channel = new BroadcastChannel(channelName);
2361
+ const handler = (ev) => setLast(ev.data);
2362
+ channel.addEventListener("message", handler);
2363
+ function post(message) {
2364
+ channel.postMessage(message);
2365
+ }
2366
+ function dispose() {
2367
+ channel.removeEventListener("message", handler);
2368
+ channel.close();
2369
+ }
2370
+ return { last, post, dispose };
2371
+ }
2372
+
2373
+ // src/browser/fullscreen.ts
2374
+ function fullscreen() {
2375
+ if (typeof document === "undefined") {
2376
+ const [isFullscreen2] = signal(false);
2377
+ const [element2] = signal(null);
2378
+ return {
2379
+ isFullscreen: isFullscreen2,
2380
+ element: element2,
2381
+ enter: async () => {
2382
+ },
2383
+ exit: async () => {
2384
+ },
2385
+ toggle: async () => {
2386
+ },
2387
+ dispose: () => {
2388
+ }
2389
+ };
2390
+ }
2391
+ const [isFullscreen, setIsFullscreen] = signal(!!document.fullscreenElement);
2392
+ const [element, setElement] = signal(document.fullscreenElement);
2393
+ const handler = () => {
2394
+ setIsFullscreen(!!document.fullscreenElement);
2395
+ setElement(document.fullscreenElement);
2396
+ };
2397
+ document.addEventListener("fullscreenchange", handler);
2398
+ async function enter(el) {
2399
+ if (!document.fullscreenElement && el.requestFullscreen) {
2400
+ await el.requestFullscreen();
2401
+ }
2402
+ }
2403
+ async function exit() {
2404
+ if (document.fullscreenElement && document.exitFullscreen) {
2405
+ await document.exitFullscreen();
2406
+ }
2407
+ }
2408
+ async function toggle(el) {
2409
+ if (document.fullscreenElement) await exit();
2410
+ else await enter(el);
2411
+ }
2412
+ function dispose() {
2413
+ document.removeEventListener("fullscreenchange", handler);
2414
+ }
2415
+ return { isFullscreen, element, enter, exit, toggle, dispose };
2416
+ }
2417
+
2418
+ // src/browser/wakeLock.ts
2419
+ function wakeLock() {
2420
+ const [active, setActive] = signal(false);
2421
+ if (typeof navigator === "undefined" || !("wakeLock" in navigator) || typeof document === "undefined") {
2422
+ return {
2423
+ active,
2424
+ request: async () => {
2425
+ },
2426
+ release: async () => {
2427
+ },
2428
+ dispose: () => {
2429
+ }
2430
+ };
2431
+ }
2432
+ const api = navigator.wakeLock;
2433
+ let sentinel = null;
2434
+ async function request() {
2435
+ try {
2436
+ sentinel = await api.request("screen");
2437
+ setActive(true);
2438
+ sentinel.addEventListener("release", () => {
2439
+ setActive(false);
2440
+ });
2441
+ } catch {
2442
+ setActive(false);
2443
+ }
2444
+ }
2445
+ async function release() {
2446
+ if (sentinel && !sentinel.released) {
2447
+ await sentinel.release();
2448
+ }
2449
+ sentinel = null;
2450
+ setActive(false);
2451
+ }
2452
+ const onVisibility = () => {
2453
+ if (sentinel?.released && !document.hidden) {
2454
+ void request();
2455
+ }
2456
+ };
2457
+ document.addEventListener("visibilitychange", onVisibility);
2458
+ function dispose() {
2459
+ document.removeEventListener("visibilitychange", onVisibility);
2460
+ void release();
2461
+ }
2462
+ return { active, request, release, dispose };
2463
+ }
2464
+
2465
+ // src/browser/animationFrame.ts
2466
+ function animationFrame(options = {}) {
2467
+ const [delta, setDelta] = signal(0);
2468
+ const [elapsed, setElapsed] = signal(0);
2469
+ const [running, setRunning] = signal(false);
2470
+ if (typeof requestAnimationFrame === "undefined") {
2471
+ return {
2472
+ delta,
2473
+ elapsed,
2474
+ running,
2475
+ pause: () => {
2476
+ },
2477
+ resume: () => {
2478
+ },
2479
+ dispose: () => {
2480
+ }
2481
+ };
2482
+ }
2483
+ let id = null;
2484
+ let prev = -1;
2485
+ let start = -1;
2486
+ const minFrameMs = options.fpsLimit ? 1e3 / options.fpsLimit : 0;
2487
+ const step = (now) => {
2488
+ if (start < 0) start = now;
2489
+ const firstTick = prev < 0;
2490
+ const dt2 = firstTick ? 0 : now - prev;
2491
+ if (firstTick || dt2 >= minFrameMs) {
2492
+ setDelta(dt2);
2493
+ setElapsed(now - start);
2494
+ prev = now;
2495
+ }
2496
+ id = requestAnimationFrame(step);
2497
+ };
2498
+ function resume() {
2499
+ if (id !== null) return;
2500
+ setRunning(true);
2501
+ id = requestAnimationFrame(step);
2502
+ }
2503
+ function pause() {
2504
+ if (id !== null) {
2505
+ cancelAnimationFrame(id);
2506
+ id = null;
2507
+ }
2508
+ setRunning(false);
2509
+ prev = -1;
2510
+ start = -1;
2511
+ }
2512
+ function dispose() {
2513
+ pause();
2514
+ }
2515
+ if (options.immediate !== false) resume();
2516
+ return { delta, elapsed, running, pause, resume, dispose };
2517
+ }
2518
+
2519
+ // src/browser/mutationObserver.ts
2520
+ function mutationObserver(target, options = { childList: true, subtree: true }) {
2521
+ const [records, setRecords] = signal([]);
2522
+ if (typeof MutationObserver === "undefined") {
2523
+ return { records, dispose: () => {
2524
+ } };
2525
+ }
2526
+ const observer = new MutationObserver((batch2) => {
2527
+ setRecords(batch2);
2528
+ });
2529
+ observer.observe(target, options);
2530
+ function dispose() {
2531
+ observer.disconnect();
2532
+ }
2533
+ return { records, dispose };
2534
+ }
2535
+
2536
+ // src/browser/bounds.ts
2537
+ var ZERO = {
2538
+ x: 0,
2539
+ y: 0,
2540
+ width: 0,
2541
+ height: 0,
2542
+ top: 0,
2543
+ left: 0,
2544
+ right: 0,
2545
+ bottom: 0
2546
+ };
2547
+ function readRect(el) {
2548
+ const r = el.getBoundingClientRect();
2549
+ return {
2550
+ x: r.x,
2551
+ y: r.y,
2552
+ width: r.width,
2553
+ height: r.height,
2554
+ top: r.top,
2555
+ left: r.left,
2556
+ right: r.right,
2557
+ bottom: r.bottom
2558
+ };
2559
+ }
2560
+ function bounds(target) {
2561
+ const [rect2, setRect] = signal(ZERO);
2562
+ if (typeof window === "undefined" || !target) {
2563
+ return {
2564
+ rect: rect2,
2565
+ refresh: () => {
2566
+ },
2567
+ dispose: () => {
2568
+ }
2569
+ };
2570
+ }
2571
+ function refresh() {
2572
+ setRect(readRect(target));
2573
+ }
2574
+ refresh();
2575
+ let resizeObserver = null;
2576
+ if (typeof ResizeObserver !== "undefined") {
2577
+ resizeObserver = new ResizeObserver(refresh);
2578
+ resizeObserver.observe(target);
2579
+ }
2580
+ const onScroll = () => refresh();
2581
+ window.addEventListener("scroll", onScroll, { passive: true, capture: true });
2582
+ function dispose() {
2583
+ resizeObserver?.disconnect();
2584
+ window.removeEventListener("scroll", onScroll, { capture: true });
2585
+ }
2586
+ return { rect: rect2, refresh, dispose };
2587
+ }
2588
+
2589
+ // src/browser/keyboard.ts
2590
+ function keyboard(options = {}) {
2591
+ const [pressed, setPressed] = signal(/* @__PURE__ */ new Set());
2592
+ if (typeof window === "undefined") {
2593
+ return {
2594
+ pressed,
2595
+ isPressed: () => false,
2596
+ dispose: () => {
2597
+ }
2598
+ };
2599
+ }
2600
+ const target = options.target ?? window;
2601
+ const filter = options.keys ? new Set(options.keys) : null;
2602
+ const onDown = (e) => {
2603
+ if (filter && !filter.has(e.key)) return;
2604
+ setPressed((prev) => {
2605
+ if (prev.has(e.key)) return prev;
2606
+ const next = new Set(prev);
2607
+ next.add(e.key);
2608
+ return next;
2609
+ });
2610
+ };
2611
+ const onUp = (e) => {
2612
+ if (filter && !filter.has(e.key)) return;
2613
+ setPressed((prev) => {
2614
+ if (!prev.has(e.key)) return prev;
2615
+ const next = new Set(prev);
2616
+ next.delete(e.key);
2617
+ return next;
2618
+ });
2619
+ };
2620
+ const onBlur = () => setPressed(/* @__PURE__ */ new Set());
2621
+ target.addEventListener("keydown", onDown);
2622
+ target.addEventListener("keyup", onUp);
2623
+ window.addEventListener("blur", onBlur);
2624
+ function isPressed(key) {
2625
+ return pressed().has(key);
2626
+ }
2627
+ function dispose() {
2628
+ target.removeEventListener("keydown", onDown);
2629
+ target.removeEventListener("keyup", onUp);
2630
+ window.removeEventListener("blur", onBlur);
2631
+ }
2632
+ return { pressed, isPressed, dispose };
2633
+ }
2634
+
2635
+ // src/browser/speech.ts
2636
+ function speech() {
2637
+ const [speaking, setSpeaking] = signal(false);
2638
+ const [paused, setPaused] = signal(false);
2639
+ if (typeof window === "undefined" || typeof window.speechSynthesis === "undefined") {
2640
+ return {
2641
+ speaking,
2642
+ paused,
2643
+ speak: () => {
2644
+ },
2645
+ pause: () => {
2646
+ },
2647
+ resume: () => {
2648
+ },
2649
+ cancel: () => {
2650
+ },
2651
+ dispose: () => {
2652
+ }
2653
+ };
2654
+ }
2655
+ const synth = window.speechSynthesis;
2656
+ const interval = setInterval(() => {
2657
+ setSpeaking(synth.speaking);
2658
+ setPaused(synth.paused);
2659
+ }, 200);
2660
+ function speak(text2, options = {}) {
2661
+ const u2 = new SpeechSynthesisUtterance(text2);
2662
+ if (options.lang) u2.lang = options.lang;
2663
+ if (options.rate != null) u2.rate = options.rate;
2664
+ if (options.pitch != null) u2.pitch = options.pitch;
2665
+ if (options.volume != null) u2.volume = options.volume;
2666
+ if (options.voice) {
2667
+ const voices = synth.getVoices();
2668
+ const match = voices.find((v) => v.name === options.voice);
2669
+ if (match) u2.voice = match;
2670
+ }
2671
+ u2.addEventListener("start", () => setSpeaking(true));
2672
+ u2.addEventListener("end", () => {
2673
+ setSpeaking(false);
2674
+ setPaused(false);
2675
+ });
2676
+ u2.addEventListener("error", () => {
2677
+ setSpeaking(false);
2678
+ setPaused(false);
2679
+ });
2680
+ synth.speak(u2);
2681
+ }
2682
+ function dispose() {
2683
+ clearInterval(interval);
2684
+ synth.cancel();
2685
+ }
2686
+ return {
2687
+ speaking,
2688
+ paused,
2689
+ speak,
2690
+ pause: () => synth.pause(),
2691
+ resume: () => synth.resume(),
2692
+ cancel: () => synth.cancel(),
2693
+ dispose
2694
+ };
2695
+ }
2696
+
2697
+ // src/browser/gamepad.ts
2698
+ function gamepad() {
2699
+ const [pads, setPads] = signal([]);
2700
+ if (typeof window === "undefined" || typeof navigator === "undefined" || typeof navigator.getGamepads !== "function") {
2701
+ return { pads, dispose: () => {
2702
+ } };
2703
+ }
2704
+ let rafId = null;
2705
+ function snapshot(pad) {
2706
+ return {
2707
+ index: pad.index,
2708
+ id: pad.id,
2709
+ connected: pad.connected,
2710
+ buttons: pad.buttons.map((b2) => ({ pressed: b2.pressed, value: b2.value })),
2711
+ axes: [...pad.axes]
2712
+ };
2713
+ }
2714
+ function equal(a2, b2) {
2715
+ if (a2.length !== b2.length) return false;
2716
+ for (let i2 = 0; i2 < a2.length; i2++) {
2717
+ const pa = a2[i2];
2718
+ const pb = b2[i2];
2719
+ if (pa.index !== pb.index || pa.connected !== pb.connected) return false;
2720
+ if (pa.buttons.length !== pb.buttons.length) return false;
2721
+ for (let j = 0; j < pa.buttons.length; j++) {
2722
+ if (pa.buttons[j].pressed !== pb.buttons[j].pressed) return false;
2723
+ if (pa.buttons[j].value !== pb.buttons[j].value) return false;
2724
+ }
2725
+ if (pa.axes.length !== pb.axes.length) return false;
2726
+ for (let j = 0; j < pa.axes.length; j++) {
2727
+ if (pa.axes[j] !== pb.axes[j]) return false;
2728
+ }
2729
+ }
2730
+ return true;
2731
+ }
2732
+ function poll() {
2733
+ const raw = navigator.getGamepads();
2734
+ const snap = Array.from(raw).filter((g2) => g2 !== null).map(snapshot);
2735
+ const current = pads();
2736
+ if (!equal(current, snap)) setPads(snap);
2737
+ rafId = requestAnimationFrame(poll);
2738
+ }
2739
+ function startPolling() {
2740
+ if (rafId === null) poll();
2741
+ }
2742
+ function stopPolling() {
2743
+ if (rafId !== null) {
2744
+ cancelAnimationFrame(rafId);
2745
+ rafId = null;
2746
+ }
2747
+ }
2748
+ const onConnect = () => startPolling();
2749
+ const onDisconnect = () => {
2750
+ const raw = navigator.getGamepads();
2751
+ const hasAny = Array.from(raw).some((g2) => g2 !== null);
2752
+ if (!hasAny) stopPolling();
2753
+ };
2754
+ window.addEventListener("gamepadconnected", onConnect);
2755
+ window.addEventListener("gamepaddisconnected", onDisconnect);
2756
+ const initial = Array.from(navigator.getGamepads()).some((g2) => g2 !== null);
2757
+ if (initial) startPolling();
2758
+ function dispose() {
2759
+ stopPolling();
2760
+ window.removeEventListener("gamepadconnected", onConnect);
2761
+ window.removeEventListener("gamepaddisconnected", onDisconnect);
2762
+ }
2763
+ return { pads, dispose };
2764
+ }
2765
+
2766
+ // src/browser/pointerLock.ts
2767
+ function pointerLock() {
2768
+ const [locked, setLocked] = signal(false);
2769
+ if (typeof document === "undefined") {
2770
+ return {
2771
+ locked,
2772
+ request: () => {
2773
+ },
2774
+ exit: () => {
2775
+ },
2776
+ dispose: () => {
2777
+ }
2778
+ };
2779
+ }
2780
+ const handler = () => {
2781
+ setLocked(!!document.pointerLockElement);
2782
+ };
2783
+ document.addEventListener("pointerlockchange", handler);
2784
+ function request(element) {
2785
+ if (typeof element.requestPointerLock === "function") {
2786
+ element.requestPointerLock();
2787
+ }
2788
+ }
2789
+ function exit() {
2790
+ if (typeof document.exitPointerLock === "function") {
2791
+ document.exitPointerLock();
2792
+ }
2793
+ }
2794
+ function dispose() {
2795
+ document.removeEventListener("pointerlockchange", handler);
2796
+ }
2797
+ return { locked, request, exit, dispose };
2798
+ }
2799
+
2800
+ // src/browser/vibrate.ts
2801
+ function vibrate(pattern2) {
2802
+ if (typeof navigator === "undefined" || typeof navigator.vibrate !== "function") {
2803
+ return false;
2804
+ }
2805
+ return navigator.vibrate(pattern2);
2806
+ }
2807
+
2808
+ // src/browser/favicon.ts
2809
+ function favicon(url) {
2810
+ if (typeof document === "undefined") return;
2811
+ let link2 = document.querySelector("link[rel='icon']");
2812
+ if (!link2) {
2813
+ link2 = document.createElement("link");
2814
+ link2.rel = "icon";
2815
+ document.head.appendChild(link2);
2816
+ }
2817
+ link2.href = url;
2818
+ }
2819
+ function svgFavicon(svg2) {
2820
+ return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg2)}`;
2821
+ }
2822
+
2823
+ // src/browser/textSelection.ts
2824
+ function textSelection() {
2825
+ const [text2, setText] = signal("");
2826
+ const [rect2, setRect] = signal(null);
2827
+ if (typeof document === "undefined") {
2828
+ return {
2829
+ text: text2,
2830
+ rect: rect2,
2831
+ hasSelection: () => false,
2832
+ clear: () => {
2833
+ },
2834
+ dispose: () => {
2835
+ }
2836
+ };
2837
+ }
2838
+ const handler = () => {
2839
+ const sel = document.getSelection();
2840
+ if (!sel || sel.rangeCount === 0 || sel.isCollapsed) {
2841
+ setText("");
2842
+ setRect(null);
2843
+ return;
2844
+ }
2845
+ setText(sel.toString());
2846
+ try {
2847
+ const r = sel.getRangeAt(0).getBoundingClientRect();
2848
+ setRect(r.width > 0 || r.height > 0 ? r : null);
2849
+ } catch {
2850
+ setRect(null);
2851
+ }
2852
+ };
2853
+ document.addEventListener("selectionchange", handler);
2854
+ function clear() {
2855
+ const sel = document.getSelection();
2856
+ sel?.removeAllRanges();
2857
+ setText("");
2858
+ setRect(null);
2859
+ }
2860
+ function dispose() {
2861
+ document.removeEventListener("selectionchange", handler);
2862
+ }
2863
+ return {
2864
+ text: text2,
2865
+ rect: rect2,
2866
+ hasSelection: () => text2().length > 0,
2867
+ clear,
2868
+ dispose
2869
+ };
2870
+ }
2871
+
2872
+ // src/browser/imageLoader.ts
2873
+ function imageLoader(src) {
2874
+ const [status, setStatus] = signal("pending");
2875
+ const [image, setImage] = signal(null);
2876
+ const [width, setWidth] = signal(0);
2877
+ const [height, setHeight] = signal(0);
2878
+ if (typeof Image === "undefined") {
2879
+ return {
2880
+ status,
2881
+ image,
2882
+ width,
2883
+ height,
2884
+ dispose: () => {
2885
+ }
2886
+ };
2887
+ }
2888
+ let current = null;
2889
+ let disposed = false;
2890
+ function start(url) {
2891
+ if (current) {
2892
+ current.onload = null;
2893
+ current.onerror = null;
2894
+ }
2895
+ setStatus("pending");
2896
+ setImage(null);
2897
+ const img2 = new Image();
2898
+ current = img2;
2899
+ img2.onload = () => {
2900
+ if (disposed || current !== img2) return;
2901
+ setImage(img2);
2902
+ setWidth(img2.naturalWidth);
2903
+ setHeight(img2.naturalHeight);
2904
+ setStatus("loaded");
2905
+ };
2906
+ img2.onerror = () => {
2907
+ if (disposed || current !== img2) return;
2908
+ setStatus("error");
2909
+ };
2910
+ img2.src = url;
2911
+ }
2912
+ if (typeof src === "function") {
2913
+ start(src());
2914
+ } else {
2915
+ start(src);
2916
+ }
2917
+ function dispose() {
2918
+ disposed = true;
2919
+ if (current) {
2920
+ current.onload = null;
2921
+ current.onerror = null;
2922
+ current = null;
2923
+ }
2924
+ }
2925
+ return { status, image, width, height, dispose };
2926
+ }
2927
+
2106
2928
  // src/patterns/machine.ts
2107
2929
  function machine(config) {
2108
2930
  const [state, setState] = signal(config.initial);
@@ -2133,8 +2955,13 @@ function machine(config) {
2133
2955
  stateDef.exit(ctx);
2134
2956
  }
2135
2957
  if (action) {
2136
- const patch = action(ctx);
2137
- setContext({ ...ctx, ...patch });
2958
+ const rawPatch = action(ctx);
2959
+ const next = { ...ctx };
2960
+ for (const key of Object.keys(rawPatch)) {
2961
+ if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
2962
+ next[key] = rawPatch[key];
2963
+ }
2964
+ setContext(next);
2138
2965
  }
2139
2966
  setState(target);
2140
2967
  const targetDef = config.states[target];
@@ -2167,6 +2994,7 @@ function persisted(key, initial, options = {}) {
2167
2994
  const deserialize = options.deserialize || JSON.parse;
2168
2995
  const encrypt = options.encrypt;
2169
2996
  const decrypt = options.decrypt;
2997
+ const syncTabs = options.session ? false : options.syncTabs ?? true;
2170
2998
  let restored = initial;
2171
2999
  try {
2172
3000
  let raw = storage.getItem(key);
@@ -2178,8 +3006,10 @@ function persisted(key, initial, options = {}) {
2178
3006
  } catch {
2179
3007
  }
2180
3008
  const [value, setValue] = signal(restored);
3009
+ let applyingFromStorage = false;
2181
3010
  effect(() => {
2182
3011
  const current = value();
3012
+ if (applyingFromStorage) return;
2183
3013
  try {
2184
3014
  let serialized = serialize(current);
2185
3015
  if (encrypt) serialized = encrypt(serialized);
@@ -2187,6 +3017,47 @@ function persisted(key, initial, options = {}) {
2187
3017
  } catch {
2188
3018
  }
2189
3019
  });
3020
+ let storageListener = null;
3021
+ if (syncTabs && typeof window !== "undefined") {
3022
+ storageListener = (e) => {
3023
+ if (e.storageArea !== storage || e.key !== key) return;
3024
+ if (e.newValue === null) {
3025
+ applyingFromStorage = true;
3026
+ try {
3027
+ setValue(initial);
3028
+ } finally {
3029
+ applyingFromStorage = false;
3030
+ }
3031
+ return;
3032
+ }
3033
+ try {
3034
+ let raw = e.newValue;
3035
+ if (decrypt) raw = decrypt(raw);
3036
+ const parsed = deserialize(raw);
3037
+ if (options.validate && !options.validate(parsed)) return;
3038
+ applyingFromStorage = true;
3039
+ try {
3040
+ setValue(parsed);
3041
+ } finally {
3042
+ applyingFromStorage = false;
3043
+ }
3044
+ } catch {
3045
+ }
3046
+ };
3047
+ window.addEventListener("storage", storageListener);
3048
+ }
3049
+ const dispose = () => {
3050
+ if (storageListener && typeof window !== "undefined") {
3051
+ window.removeEventListener("storage", storageListener);
3052
+ storageListener = null;
3053
+ }
3054
+ };
3055
+ Object.defineProperty(setValue, "dispose", {
3056
+ value: dispose,
3057
+ enumerable: false,
3058
+ writable: false,
3059
+ configurable: false
3060
+ });
2190
3061
  return [value, setValue];
2191
3062
  }
2192
3063
 
@@ -2353,17 +3224,6 @@ function globalStore(config) {
2353
3224
  return { getState, select: select3, dispatch, subscribe, reset };
2354
3225
  }
2355
3226
 
2356
- // src/patterns/primitives.ts
2357
- function createSignal(value) {
2358
- return signal(value);
2359
- }
2360
- function createMemo(fn) {
2361
- return derived(fn);
2362
- }
2363
- function createEffect(fn) {
2364
- return effect(fn);
2365
- }
2366
-
2367
3227
  // src/patterns/hoc.ts
2368
3228
  function withWrapper(WrappedComponent, wrapper) {
2369
3229
  return (props) => wrapper(WrappedComponent, props);
@@ -2376,9 +3236,6 @@ function compose(...wrappers) {
2376
3236
  }
2377
3237
 
2378
3238
  // src/patterns/composable.ts
2379
- function composable(setup) {
2380
- return setup;
2381
- }
2382
3239
  function RenderProp(props) {
2383
3240
  return props.render(props.data());
2384
3241
  }
@@ -3375,8 +4232,25 @@ function announce(message, priority = "polite") {
3375
4232
 
3376
4233
  // src/ui/scopedStyle.ts
3377
4234
  var scopeCounter = 0;
4235
+ function decodeCssEscapes(css) {
4236
+ return css.replace(/\\([0-9a-f]{1,6})[ \t\n\r\f]?|\\([^\n])/gi, (_match, hex, ch) => {
4237
+ if (hex) {
4238
+ const code2 = Number.parseInt(hex, 16);
4239
+ if (Number.isFinite(code2) && code2 > 0 && code2 <= 1114111) {
4240
+ try {
4241
+ return String.fromCodePoint(code2);
4242
+ } catch {
4243
+ return "";
4244
+ }
4245
+ }
4246
+ return "";
4247
+ }
4248
+ return ch || "";
4249
+ });
4250
+ }
3378
4251
  function sanitizeCSS(css) {
3379
- let sanitized = css.replace(/@import\s+[^;]+;/gi, "/* @import removed */");
4252
+ let sanitized = decodeCssEscapes(css);
4253
+ sanitized = sanitized.replace(/@import\s+[^;]+;/gi, "/* @import removed */");
3380
4254
  sanitized = sanitized.replace(/url\s*\(\s*(?:"[^"]*"|'[^']*'|[^)]*)\s*\)/gi, "/* url() removed */");
3381
4255
  sanitized = sanitized.replace(/expression\s*\(\s*(?:"[^"]*"|'[^']*'|[^)]*)\s*\)/gi, "/* expression() removed */");
3382
4256
  sanitized = sanitized.replace(/-moz-binding\s*:[^;]+;/gi, "/* -moz-binding removed */");
@@ -3426,31 +4300,22 @@ function removeScopedStyle(scopeId) {
3426
4300
  if (el) el.remove();
3427
4301
  }
3428
4302
 
3429
- // src/utils/sanitize.ts
3430
- function sanitizeUrl(url) {
3431
- const trimmed = url.replace(/[\x00-\x20\x7f-\x9f]+/g, "").trim();
3432
- if (!trimmed) return "";
3433
- const lower = trimmed.toLowerCase();
3434
- if (lower.startsWith("javascript:") || lower.startsWith("data:") || lower.startsWith("vbscript:") || lower.startsWith("blob:")) {
3435
- return "";
3436
- }
3437
- return trimmed;
3438
- }
3439
- function sanitizeCSSValue(value) {
3440
- const lower = value.toLowerCase().replace(/\s+/g, "");
3441
- if (lower.includes("url(") || lower.includes("expression(") || lower.includes("javascript:") || lower.includes("-moz-binding")) {
3442
- return "";
3443
- }
3444
- return value;
3445
- }
3446
- var URL_ATTRIBUTES = /* @__PURE__ */ new Set(["href", "src", "action", "formaction", "cite", "poster", "background", "srcset"]);
3447
- function isUrlAttribute(attr) {
3448
- return URL_ATTRIBUTES.has(attr);
3449
- }
3450
-
3451
4303
  // src/reactivity/bindAttribute.ts
3452
4304
  var _isDev4 = isDev();
4305
+ function isEventHandlerAttr(name) {
4306
+ if (name.length < 3) return false;
4307
+ const lower = name.toLowerCase();
4308
+ return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
4309
+ }
3453
4310
  function bindAttribute(el, attr, getter) {
4311
+ if (isEventHandlerAttr(attr)) {
4312
+ if (_isDev4)
4313
+ devWarn(
4314
+ `bindAttribute: refusing to bind event-handler attribute "${attr}". Use on:{ ${attr.slice(2)}: fn } instead.`
4315
+ );
4316
+ return () => {
4317
+ };
4318
+ }
3454
4319
  function commit() {
3455
4320
  let value;
3456
4321
  try {
@@ -4374,11 +5239,34 @@ function preloadModules(urls) {
4374
5239
  }
4375
5240
 
4376
5241
  // src/platform/head.ts
4377
- var HEAD_URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "content"]);
5242
+ var HEAD_URL_ATTRS = /* @__PURE__ */ new Set(["href", "src"]);
4378
5243
  function sanitizeHeadAttr(key, value) {
4379
5244
  if (HEAD_URL_ATTRS.has(key)) return sanitizeUrl(value);
4380
5245
  return value;
4381
5246
  }
5247
+ function isDangerousMetaRefresh(metaProps) {
5248
+ const httpEquiv = metaProps["http-equiv"];
5249
+ if (typeof httpEquiv !== "string") return false;
5250
+ if (httpEquiv.toLowerCase() !== "refresh") return false;
5251
+ const content = metaProps.content;
5252
+ if (typeof content !== "string") return false;
5253
+ const normalized = content.replace(/[\x00-\x20\x7f-\x9f]+/g, "").toLowerCase();
5254
+ return normalized.includes("url=javascript:") || normalized.includes("url=data:") || normalized.includes("url=vbscript:") || normalized.includes("url=blob:");
5255
+ }
5256
+ var SAFE_HEAD_ATTR_NAME = /^[A-Za-z_:][-A-Za-z0-9_.:]*$/;
5257
+ function isEventHandlerAttr2(name) {
5258
+ if (name.length < 3) return false;
5259
+ const lower = name.toLowerCase();
5260
+ return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
5261
+ }
5262
+ function isSafeHeadAttr(name) {
5263
+ if (!SAFE_HEAD_ATTR_NAME.test(name)) return false;
5264
+ if (isEventHandlerAttr2(name)) return false;
5265
+ return true;
5266
+ }
5267
+ function escapeScriptJsonLocal(json) {
5268
+ return json.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
5269
+ }
4382
5270
  function Head(props) {
4383
5271
  const anchor = document.createComment("sibu-head");
4384
5272
  const managedElements = [];
@@ -4405,15 +5293,17 @@ function Head(props) {
4405
5293
  }
4406
5294
  if (props.meta) {
4407
5295
  for (const metaProps of props.meta) {
5296
+ if (isDangerousMetaRefresh(metaProps)) continue;
4408
5297
  const el = document.createElement("meta");
4409
5298
  for (const [key, value] of Object.entries(metaProps)) {
5299
+ if (!isSafeHeadAttr(key)) continue;
4410
5300
  if (typeof value === "function") {
4411
5301
  const cleanupFn = effect(() => {
4412
- el.setAttribute(key, value());
5302
+ el.setAttribute(key, sanitizeHeadAttr(key, value()));
4413
5303
  });
4414
5304
  effectCleanups.push(cleanupFn);
4415
5305
  } else {
4416
- el.setAttribute(key, value);
5306
+ el.setAttribute(key, sanitizeHeadAttr(key, value));
4417
5307
  }
4418
5308
  }
4419
5309
  document.head.appendChild(el);
@@ -4424,6 +5314,7 @@ function Head(props) {
4424
5314
  for (const linkProps of props.link) {
4425
5315
  const el = document.createElement("link");
4426
5316
  for (const [key, value] of Object.entries(linkProps)) {
5317
+ if (!isSafeHeadAttr(key)) continue;
4427
5318
  el.setAttribute(key, sanitizeHeadAttr(key, value));
4428
5319
  }
4429
5320
  document.head.appendChild(el);
@@ -4434,6 +5325,7 @@ function Head(props) {
4434
5325
  for (const scriptProps of props.script) {
4435
5326
  const el = document.createElement("script");
4436
5327
  for (const [key, value] of Object.entries(scriptProps)) {
5328
+ if (!isSafeHeadAttr(key)) continue;
4437
5329
  el.setAttribute(key, sanitizeHeadAttr(key, value));
4438
5330
  }
4439
5331
  document.head.appendChild(el);
@@ -4444,7 +5336,10 @@ function Head(props) {
4444
5336
  const existing = document.head.querySelector("base");
4445
5337
  if (existing) existing.remove();
4446
5338
  const el = document.createElement("base");
4447
- if (props.base.href) el.href = props.base.href;
5339
+ if (props.base.href) {
5340
+ const safeHref = sanitizeUrl(props.base.href);
5341
+ if (safeHref) el.href = safeHref;
5342
+ }
4448
5343
  if (props.base.target) el.target = props.base.target;
4449
5344
  document.head.appendChild(el);
4450
5345
  managedElements.push(el);
@@ -4459,7 +5354,7 @@ function setStructuredData(data2) {
4459
5354
  const script2 = document.createElement("script");
4460
5355
  script2.type = "application/ld+json";
4461
5356
  script2.setAttribute("data-sibu", "true");
4462
- script2.textContent = JSON.stringify(data2);
5357
+ script2.textContent = escapeScriptJsonLocal(JSON.stringify(data2));
4463
5358
  document.head.appendChild(script2);
4464
5359
  }
4465
5360
  function setCanonical(url) {
@@ -4474,12 +5369,39 @@ function setCanonical(url) {
4474
5369
 
4475
5370
  // src/platform/ssr.ts
4476
5371
  var _isDev5 = isDev();
5372
+ var SAFE_ATTR_NAME = /^[A-Za-z_:][-A-Za-z0-9_.:]*$/;
5373
+ function isSafeAttrName(name) {
5374
+ return SAFE_ATTR_NAME.test(name);
5375
+ }
5376
+ function isEventHandlerAttr3(name) {
5377
+ if (name.length < 3) return false;
5378
+ const lower = name.toLowerCase();
5379
+ return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
5380
+ }
5381
+ var URL_ATTRS = /* @__PURE__ */ new Set([
5382
+ "href",
5383
+ "src",
5384
+ "action",
5385
+ "formaction",
5386
+ "cite",
5387
+ "poster",
5388
+ "background",
5389
+ "srcset",
5390
+ "ping",
5391
+ "manifest",
5392
+ "data",
5393
+ "xlink:href"
5394
+ ]);
4477
5395
  function ssrErrorComment(err) {
4478
5396
  if (_isDev5) {
4479
- return `<!--SSR error: ${escapeHtml(err instanceof Error ? err.message : String(err))}-->`;
5397
+ const msg = escapeHtml(err instanceof Error ? err.message : String(err));
5398
+ return `<!--SSR error: ${safeCommentText(msg)}-->`;
4480
5399
  }
4481
5400
  return "<!--SSR error-->";
4482
5401
  }
5402
+ function safeCommentText(text2) {
5403
+ return text2.replace(/-->/g, "--&gt;").replace(/--!>/g, "--!&gt;").replace(/<!--/g, "&lt;!--").replace(/--$/g, "--&#45;");
5404
+ }
4483
5405
  var VOID_ELEMENTS = /* @__PURE__ */ new Set([
4484
5406
  "area",
4485
5407
  "base",
@@ -4510,16 +5432,30 @@ function renderToString(element) {
4510
5432
  return escapeHtml(element.textContent || "");
4511
5433
  }
4512
5434
  if (element.nodeType === 8) {
4513
- const content = (element.textContent || "").replace(/-->/g, "--&gt;");
4514
- return `<!--${content}-->`;
5435
+ return `<!--${safeCommentText(element.textContent || "")}-->`;
4515
5436
  }
4516
5437
  if (!(element instanceof HTMLElement)) {
4517
- return element.textContent || "";
5438
+ return escapeHtml(element.textContent || "");
4518
5439
  }
4519
5440
  const tag = element.tagName.toLowerCase();
5441
+ if (tag === "script" || tag === "style") {
5442
+ return _isDev5 ? `<!--ssr:${tag}-stripped-->` : "";
5443
+ }
5444
+ if (!/^[a-z][a-z0-9-]*$/i.test(tag)) {
5445
+ return _isDev5 ? "<!--ssr:invalid-tag-->" : "";
5446
+ }
4520
5447
  let html2 = `<${tag}`;
4521
5448
  for (const attr of Array.from(element.attributes)) {
4522
- html2 += ` ${attr.name}="${escapeAttr(attr.value)}"`;
5449
+ const rawName = attr.name;
5450
+ if (!isSafeAttrName(rawName)) continue;
5451
+ if (isEventHandlerAttr3(rawName)) continue;
5452
+ const lowerName = rawName.toLowerCase();
5453
+ let value = attr.value;
5454
+ if (URL_ATTRS.has(lowerName)) {
5455
+ value = sanitizeUrl(value);
5456
+ if (!value) continue;
5457
+ }
5458
+ html2 += ` ${rawName}="${escapeAttr(value)}"`;
4523
5459
  }
4524
5460
  if (element.dataset && !element.dataset.sibuHydrate) {
4525
5461
  html2 += ` data-sibu-ssr="true"`;
@@ -4538,8 +5474,25 @@ function renderToString(element) {
4538
5474
  html2 += `</${tag}>`;
4539
5475
  return html2;
4540
5476
  }
4541
- function hydrate(component, container) {
5477
+ function hydrate(component, container, options = {}) {
4542
5478
  const clientTree = component();
5479
+ if (options.diagnostics) {
5480
+ const mismatches = [];
5481
+ collectMismatches(container.firstElementChild, clientTree, "", mismatches);
5482
+ if (mismatches.length > 0) {
5483
+ const first = mismatches[0];
5484
+ if (options.onMismatch) {
5485
+ options.onMismatch(first);
5486
+ } else if (_isDev5) {
5487
+ console.warn(
5488
+ `[Sibu hydration] ${first.message}
5489
+ at ${first.path}
5490
+ server: ${first.serverValue}
5491
+ client: ${first.clientValue}`
5492
+ );
5493
+ }
5494
+ }
5495
+ }
4543
5496
  hydrateNode(container.firstElementChild, clientTree);
4544
5497
  container.setAttribute("data-sibu-hydrated", "true");
4545
5498
  }
@@ -4551,9 +5504,119 @@ function hydrateNode(serverNode, clientNode) {
4551
5504
  hydrateNode(serverChildren[i2], clientChildren[i2]);
4552
5505
  }
4553
5506
  }
5507
+ function collectMismatches(serverNode, clientNode, path2, out, max2 = 5) {
5508
+ if (out.length >= max2) return;
5509
+ const nodePath = path2 || clientNode?.tagName?.toLowerCase() || "(root)";
5510
+ if (!serverNode && clientNode) {
5511
+ out.push({
5512
+ kind: "child-count",
5513
+ path: nodePath,
5514
+ serverValue: "(missing)",
5515
+ clientValue: clientNode.tagName.toLowerCase(),
5516
+ message: "Client rendered a node that the server did not emit."
5517
+ });
5518
+ return;
5519
+ }
5520
+ if (serverNode && !clientNode) {
5521
+ out.push({
5522
+ kind: "child-count",
5523
+ path: nodePath,
5524
+ serverValue: serverNode.tagName.toLowerCase(),
5525
+ clientValue: "(missing)",
5526
+ message: "Server rendered a node that the client did not produce."
5527
+ });
5528
+ return;
5529
+ }
5530
+ if (!serverNode || !clientNode) return;
5531
+ if (serverNode.tagName !== clientNode.tagName) {
5532
+ out.push({
5533
+ kind: "tag",
5534
+ path: nodePath,
5535
+ serverValue: serverNode.tagName.toLowerCase(),
5536
+ clientValue: clientNode.tagName.toLowerCase(),
5537
+ message: "Element tag mismatch \u2014 server and client disagree on the element type."
5538
+ });
5539
+ return;
5540
+ }
5541
+ const skipAttrs = /* @__PURE__ */ new Set(["data-sibu-ssr", "data-sibu-hydrated", "data-sibu-island"]);
5542
+ const serverAttrs = /* @__PURE__ */ new Map();
5543
+ for (const a2 of Array.from(serverNode.attributes)) {
5544
+ if (!skipAttrs.has(a2.name)) serverAttrs.set(a2.name, a2.value);
5545
+ }
5546
+ const clientAttrs = /* @__PURE__ */ new Map();
5547
+ for (const a2 of Array.from(clientNode.attributes)) {
5548
+ if (!skipAttrs.has(a2.name)) clientAttrs.set(a2.name, a2.value);
5549
+ }
5550
+ for (const [name, value] of serverAttrs) {
5551
+ if (out.length >= max2) return;
5552
+ if (!clientAttrs.has(name)) {
5553
+ out.push({
5554
+ kind: "attribute",
5555
+ path: `${nodePath}[${name}]`,
5556
+ serverValue: value,
5557
+ clientValue: "(missing)",
5558
+ message: `Attribute "${name}" present on server but missing on client.`
5559
+ });
5560
+ } else if (clientAttrs.get(name) !== value) {
5561
+ out.push({
5562
+ kind: "attribute",
5563
+ path: `${nodePath}[${name}]`,
5564
+ serverValue: value,
5565
+ clientValue: clientAttrs.get(name) ?? "",
5566
+ message: `Attribute "${name}" differs between server and client.`
5567
+ });
5568
+ }
5569
+ }
5570
+ for (const [name, value] of clientAttrs) {
5571
+ if (out.length >= max2) return;
5572
+ if (!serverAttrs.has(name)) {
5573
+ out.push({
5574
+ kind: "attribute",
5575
+ path: `${nodePath}[${name}]`,
5576
+ serverValue: "(missing)",
5577
+ clientValue: value,
5578
+ message: `Attribute "${name}" present on client but missing on server.`
5579
+ });
5580
+ }
5581
+ }
5582
+ const serverChildren = Array.from(serverNode.children);
5583
+ const clientChildren = Array.from(clientNode.children);
5584
+ const max22 = Math.max(serverChildren.length, clientChildren.length);
5585
+ for (let i2 = 0; i2 < max22; i2++) {
5586
+ if (out.length >= max2) return;
5587
+ const childPath = `${nodePath} > ${clientChildren[i2]?.tagName?.toLowerCase() ?? serverChildren[i2]?.tagName?.toLowerCase() ?? "?"}:nth-child(${i2 + 1})`;
5588
+ collectMismatches(serverChildren[i2] ?? null, clientChildren[i2] ?? null, childPath, out, max2);
5589
+ }
5590
+ }
4554
5591
  function trustHTML(html2) {
4555
5592
  return html2;
4556
5593
  }
5594
+ function buildAttrString(attrs, { allowEventHandlers = false } = {}) {
5595
+ if (!attrs) return "";
5596
+ const out = [];
5597
+ for (const rawKey of Object.keys(attrs)) {
5598
+ if (!Object.hasOwn(attrs, rawKey)) continue;
5599
+ if (!isSafeAttrName(rawKey)) continue;
5600
+ if (!allowEventHandlers && isEventHandlerAttr3(rawKey)) continue;
5601
+ const lowerKey = rawKey.toLowerCase();
5602
+ let value = String(attrs[rawKey]);
5603
+ if (URL_ATTRS.has(lowerKey)) {
5604
+ value = sanitizeUrl(value);
5605
+ if (!value) continue;
5606
+ }
5607
+ out.push(`${rawKey}="${escapeAttr(value)}"`);
5608
+ }
5609
+ return out.join(" ");
5610
+ }
5611
+ function isDangerousMetaRefresh2(metaProps) {
5612
+ const httpEquiv = metaProps["http-equiv"];
5613
+ if (typeof httpEquiv !== "string") return false;
5614
+ if (httpEquiv.toLowerCase() !== "refresh") return false;
5615
+ const content = metaProps.content;
5616
+ if (typeof content !== "string") return false;
5617
+ const normalized = content.replace(/[\x00-\x20\x7f-\x9f]+/g, "").toLowerCase();
5618
+ return normalized.includes("url=javascript:") || normalized.includes("url=data:") || normalized.includes("url=vbscript:") || normalized.includes("url=blob:");
5619
+ }
4557
5620
  function renderToDocument(component, options = {}) {
4558
5621
  let content;
4559
5622
  try {
@@ -4561,14 +5624,22 @@ function renderToDocument(component, options = {}) {
4561
5624
  } catch (err) {
4562
5625
  content = ssrErrorComment(err);
4563
5626
  }
4564
- const metaTags = (options.meta || []).map(
4565
- (attrs) => `<meta ${Object.entries(attrs).map(([k, v]) => `${k}="${escapeAttr(v)}"`).join(" ")} />`
4566
- ).join("\n ");
4567
- const linkTags = (options.links || []).map(
4568
- (attrs) => `<link ${Object.entries(attrs).map(([k, v]) => `${k}="${escapeAttr(v)}"`).join(" ")} />`
4569
- ).join("\n ");
4570
- const scriptTags = (options.scripts || []).map((src) => `<script src="${escapeAttr(src)}"></script>`).join("\n ");
4571
- const bodyAttrs = options.bodyAttrs ? " " + Object.entries(options.bodyAttrs).map(([k, v]) => `${k}="${escapeAttr(v)}"`).join(" ") : "";
5627
+ const metaTags = (options.meta || []).map((attrs) => {
5628
+ if (isDangerousMetaRefresh2(attrs)) return "";
5629
+ const pairs = buildAttrString(attrs);
5630
+ return pairs ? `<meta ${pairs} />` : "";
5631
+ }).filter(Boolean).join("\n ");
5632
+ const linkTags = (options.links || []).map((attrs) => {
5633
+ const pairs = buildAttrString(attrs);
5634
+ return pairs ? `<link ${pairs} />` : "";
5635
+ }).filter(Boolean).join("\n ");
5636
+ const scriptTags = (options.scripts || []).map((src) => {
5637
+ const safe = sanitizeUrl(String(src));
5638
+ if (!safe) return "";
5639
+ return `<script src="${escapeAttr(safe)}"></script>`;
5640
+ }).filter(Boolean).join("\n ");
5641
+ const bodyAttrPairs = buildAttrString(options.bodyAttrs);
5642
+ const bodyAttrs = bodyAttrPairs ? ` ${bodyAttrPairs}` : "";
4572
5643
  return `<!DOCTYPE html>
4573
5644
  <html>
4574
5645
  <head>
@@ -4601,18 +5672,34 @@ async function* renderToStream(element) {
4601
5672
  return;
4602
5673
  }
4603
5674
  if (element.nodeType === 8) {
4604
- const content = (element.textContent || "").replace(/-->/g, "--&gt;");
4605
- yield `<!--${content}-->`;
5675
+ yield `<!--${safeCommentText(element.textContent || "")}-->`;
4606
5676
  return;
4607
5677
  }
4608
5678
  if (!(element instanceof HTMLElement)) {
4609
- yield element.textContent || "";
5679
+ yield escapeHtml(element.textContent || "");
4610
5680
  return;
4611
5681
  }
4612
5682
  const tag = element.tagName.toLowerCase();
5683
+ if (tag === "script" || tag === "style") {
5684
+ if (_isDev5) yield `<!--ssr:${tag}-stripped-->`;
5685
+ return;
5686
+ }
5687
+ if (!/^[a-z][a-z0-9-]*$/i.test(tag)) {
5688
+ if (_isDev5) yield "<!--ssr:invalid-tag-->";
5689
+ return;
5690
+ }
4613
5691
  let openTag = `<${tag}`;
4614
5692
  for (const attr of Array.from(element.attributes)) {
4615
- openTag += ` ${attr.name}="${escapeAttr(attr.value)}"`;
5693
+ const rawName = attr.name;
5694
+ if (!isSafeAttrName(rawName)) continue;
5695
+ if (isEventHandlerAttr3(rawName)) continue;
5696
+ const lowerName = rawName.toLowerCase();
5697
+ let value = attr.value;
5698
+ if (URL_ATTRS.has(lowerName)) {
5699
+ value = sanitizeUrl(value);
5700
+ if (!value) continue;
5701
+ }
5702
+ openTag += ` ${rawName}="${escapeAttr(value)}"`;
4616
5703
  }
4617
5704
  if (VOID_ELEMENTS.has(tag)) {
4618
5705
  yield `${openTag} />`;
@@ -4660,8 +5747,9 @@ function hydrateIslands(container, islands) {
4660
5747
  const markers = container.querySelectorAll("[data-sibu-island]");
4661
5748
  for (const marker2 of Array.from(markers)) {
4662
5749
  const id = marker2.getAttribute("data-sibu-island") ?? "";
5750
+ if (!Object.hasOwn(islands, id)) continue;
4663
5751
  const factory = islands[id];
4664
- if (!factory) continue;
5752
+ if (typeof factory !== "function") continue;
4665
5753
  const clientTree = factory();
4666
5754
  hydrateNode(marker2, clientTree);
4667
5755
  marker2.setAttribute("data-sibu-hydrated", "true");
@@ -4673,8 +5761,9 @@ function hydrateProgressively(container, islands, options) {
4673
5761
  const cleanups = [];
4674
5762
  for (const marker2 of Array.from(markers)) {
4675
5763
  const id = marker2.getAttribute("data-sibu-island") ?? "";
5764
+ if (!Object.hasOwn(islands, id)) continue;
4676
5765
  const factory = islands[id];
4677
- if (!factory) continue;
5766
+ if (typeof factory !== "function") continue;
4678
5767
  const observer = new IntersectionObserver(
4679
5768
  (entries) => {
4680
5769
  for (const entry of entries) {
@@ -4713,24 +5802,33 @@ function ssrSuspense(props) {
4713
5802
  }));
4714
5803
  return { element: wrapper, promise };
4715
5804
  }
5805
+ var SAFE_SUSPENSE_ID = /^[A-Za-z0-9_-]+$/;
4716
5806
  function suspenseSwapScript(id, nonce) {
4717
- const safeId = id.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/</g, "\\u003c").replace(/>/g, "\\u003e");
5807
+ if (!SAFE_SUSPENSE_ID.test(id)) {
5808
+ throw new Error(
5809
+ `[SibuJS SSR] suspenseSwapScript: id must match [A-Za-z0-9_-]+ (got: ${JSON.stringify(id.slice(0, 32))})`
5810
+ );
5811
+ }
4718
5812
  const nonceAttr = nonce ? ` nonce="${escapeAttr(nonce)}"` : "";
4719
- return `<script${nonceAttr}>(function(){var t=document.getElementById("sibu-resolved-${safeId}");var f=document.querySelector('[data-sibu-suspense-id="${safeId}"]');if(t&&f){while(t.firstChild)f.appendChild(t.firstChild);t.remove();f.removeAttribute("data-sibu-suspense-id");}})()</script>`;
5813
+ return `<script${nonceAttr}>(function(){var t=document.getElementById("sibu-resolved-${id}");var f=document.querySelector('[data-sibu-suspense-id="${id}"]');if(t&&f){while(t.firstChild)f.appendChild(t.firstChild);t.remove();f.removeAttribute("data-sibu-suspense-id");}})()</script>`;
4720
5814
  }
4721
- async function* renderToSuspenseStream(element, pendingBoundaries = []) {
5815
+ async function* renderToSuspenseStream(element, pendingBoundaries = [], options) {
4722
5816
  yield* renderToStream(element);
4723
5817
  if (pendingBoundaries.length > 0) {
4724
5818
  const resolved = await Promise.all(pendingBoundaries);
4725
5819
  for (const { id, html: html2 } of resolved) {
4726
- yield `<div hidden id="sibu-resolved-${escapeAttr(id)}">${html2}</div>`;
4727
- yield suspenseSwapScript(id);
5820
+ if (!SAFE_SUSPENSE_ID.test(id)) continue;
5821
+ yield `<div hidden id="sibu-resolved-${id}">${html2}</div>`;
5822
+ yield suspenseSwapScript(id, options?.nonce);
4728
5823
  }
4729
5824
  }
4730
5825
  }
4731
5826
  var SSR_DATA_ATTR = "__SIBU_SSR_DATA__";
5827
+ function escapeScriptJson(json) {
5828
+ return json.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
5829
+ }
4732
5830
  function serializeState(state, nonce) {
4733
- const json = JSON.stringify(state).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026");
5831
+ const json = escapeScriptJson(JSON.stringify(state));
4734
5832
  const nonceAttr = nonce ? ` nonce="${escapeAttr(nonce)}"` : "";
4735
5833
  return `<script${nonceAttr}>window.${SSR_DATA_ATTR}=${json}</script>`;
4736
5834
  }
@@ -4745,7 +5843,7 @@ function escapeHtml(str) {
4745
5843
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4746
5844
  }
4747
5845
  function escapeAttr(str) {
4748
- return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5846
+ return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4749
5847
  }
4750
5848
 
4751
5849
  // src/platform/customElement.ts
@@ -5300,16 +6398,20 @@ function appendChildren(el, nodes) {
5300
6398
  var tagFactory = (tag, ns) => (first, second) => {
5301
6399
  const el = ns ? document.createElementNS(ns, tag) : document.createElement(tag);
5302
6400
  if (first === void 0) return el;
5303
- if (second === void 0 && typeof first === "string") {
6401
+ if (typeof first === "string") {
6402
+ if (second !== void 0) {
6403
+ el.setAttribute("class", first);
6404
+ appendChildren(el, second);
6405
+ return el;
6406
+ }
5304
6407
  el.textContent = first;
5305
6408
  return el;
5306
6409
  }
5307
- if (second !== void 0) {
5308
- el.setAttribute("class", first);
5309
- appendChildren(el, second);
6410
+ if (typeof first === "number") {
6411
+ el.textContent = String(first);
5310
6412
  return el;
5311
6413
  }
5312
- if (Array.isArray(first) || first instanceof Node) {
6414
+ if (Array.isArray(first) || first instanceof Node || typeof first === "function") {
5313
6415
  appendChildren(el, first);
5314
6416
  return el;
5315
6417
  }
@@ -5318,7 +6420,7 @@ var tagFactory = (tag, ns) => (first, second) => {
5318
6420
  if (pClass != null) applyClass(el, pClass);
5319
6421
  const pId = props.id;
5320
6422
  if (pId != null) el.id = pId;
5321
- const pNodes = props.nodes;
6423
+ const pNodes = second !== void 0 ? second : props.nodes;
5322
6424
  if (pNodes != null) appendChildren(el, pNodes);
5323
6425
  const pOn = props.on;
5324
6426
  if (pOn) {
@@ -6095,7 +7197,7 @@ var bundlerMetadata = {
6095
7197
  sideEffects: false,
6096
7198
  modules: {
6097
7199
  core: ["html", "mount", "each", "slots", "fragment", "catch", "portal", "directives"],
6098
- hooks: ["signal", "effect", "derived", "watch", "store", "ref", "memo", "memoFn", "array", "deepSignal"],
7200
+ hooks: ["signal", "effect", "derived", "watch", "store", "ref", "array", "deepSignal"],
6099
7201
  plugins: ["router", "i18n"],
6100
7202
  components: ["ErrorBoundary", "Loading"],
6101
7203
  ssr: ["ssr"],
@@ -8619,6 +9721,7 @@ var materialAdapter = componentAdapter(materialConfig);
8619
9721
  VirtualList,
8620
9722
  accordion,
8621
9723
  animate,
9724
+ animationFrame,
8622
9725
  announce,
8623
9726
  antdAdapter,
8624
9727
  aria,
@@ -8631,6 +9734,8 @@ var materialAdapter = componentAdapter(materialConfig);
8631
9734
  block,
8632
9735
  bounceIn,
8633
9736
  bounceOut,
9737
+ bounds,
9738
+ broadcast,
8634
9739
  bundlerMetadata,
8635
9740
  calculateDelay,
8636
9741
  chakraAdapter,
@@ -8648,7 +9753,6 @@ var materialAdapter = componentAdapter(materialConfig);
8648
9753
  combobox,
8649
9754
  compareSemVer,
8650
9755
  componentAdapter,
8651
- composable,
8652
9756
  compose,
8653
9757
  composeMiddleware,
8654
9758
  conditional,
@@ -8658,12 +9762,10 @@ var materialAdapter = componentAdapter(materialConfig);
8658
9762
  createBundle,
8659
9763
  createChunkRegistry,
8660
9764
  createDevtoolsOverlay,
8661
- createEffect,
8662
9765
  createErrorReporter,
8663
9766
  createGuard,
8664
9767
  createHMRBoundary,
8665
9768
  createISR,
8666
- createMemo,
8667
9769
  createMicroApp,
8668
9770
  createMiddlewareChain,
8669
9771
  createMigrationRunner,
@@ -8672,7 +9774,6 @@ var materialAdapter = componentAdapter(materialConfig);
8672
9774
  createProfiler,
8673
9775
  createSSRCache,
8674
9776
  createSharedScope,
8675
- createSignal,
8676
9777
  createSlots,
8677
9778
  createTestHarness,
8678
9779
  createTheme,
@@ -8704,10 +9805,12 @@ var materialAdapter = componentAdapter(materialConfig);
8704
9805
  email,
8705
9806
  enableDebug,
8706
9807
  env,
9808
+ escapeScriptJson,
8707
9809
  eventBus,
8708
9810
  executeLoader,
8709
9811
  fadeIn,
8710
9812
  fadeOut,
9813
+ favicon,
8711
9814
  fileUpload,
8712
9815
  flipIn,
8713
9816
  flushScheduler,
@@ -8716,6 +9819,8 @@ var materialAdapter = componentAdapter(materialConfig);
8716
9819
  formatCurrency,
8717
9820
  formatError,
8718
9821
  formatNumber,
9822
+ fullscreen,
9823
+ gamepad,
8719
9824
  generateStaticSite,
8720
9825
  geo,
8721
9826
  getActiveDevTools,
@@ -8734,6 +9839,7 @@ var materialAdapter = componentAdapter(materialConfig);
8734
9839
  hydrateIslands,
8735
9840
  hydrateProgressively,
8736
9841
  idle,
9842
+ imageLoader,
8737
9843
  infiniteQuery,
8738
9844
  infiniteScroll,
8739
9845
  initDevTools,
@@ -8746,6 +9852,7 @@ var materialAdapter = componentAdapter(materialConfig);
8746
9852
  isHMRAvailable,
8747
9853
  isWasmCached,
8748
9854
  island,
9855
+ keyboard,
8749
9856
  lazyChunk,
8750
9857
  lazyLoad,
8751
9858
  lazyModule,
@@ -8762,7 +9869,10 @@ var materialAdapter = componentAdapter(materialConfig);
8762
9869
  min,
8763
9870
  minLength,
8764
9871
  mobXAdapter,
9872
+ mouse,
8765
9873
  mutation,
9874
+ mutationObserver,
9875
+ network,
8766
9876
  noSideEffect,
8767
9877
  normalize,
8768
9878
  normalizedStore,
@@ -8779,6 +9889,7 @@ var materialAdapter = componentAdapter(materialConfig);
8779
9889
  persisted,
8780
9890
  phoneMask,
8781
9891
  plugin,
9892
+ pointerLock,
8782
9893
  popover,
8783
9894
  precompile,
8784
9895
  prefetch,
@@ -8830,6 +9941,7 @@ var materialAdapter = componentAdapter(materialConfig);
8830
9941
  slideIn,
8831
9942
  slideOut,
8832
9943
  socket,
9944
+ speech,
8833
9945
  spring,
8834
9946
  springSignal,
8835
9947
  ssnMask,
@@ -8841,8 +9953,11 @@ var materialAdapter = componentAdapter(materialConfig);
8841
9953
  stream,
8842
9954
  suspenseSwapScript,
8843
9955
  svgElement,
9956
+ svgFavicon,
9957
+ swipe,
8844
9958
  syncAdapter,
8845
9959
  tabs,
9960
+ textSelection,
8846
9961
  throttle,
8847
9962
  timeMask,
8848
9963
  timeline,
@@ -8857,11 +9972,16 @@ var materialAdapter = componentAdapter(materialConfig);
8857
9972
  triggerPluginUnmount,
8858
9973
  trustHTML,
8859
9974
  uniqueId,
9975
+ urlState,
8860
9976
  validateProps,
8861
9977
  validators,
9978
+ vibrate,
8862
9979
  viewTransition,
9980
+ visibility,
9981
+ wakeLock,
8863
9982
  walkDependencyGraph,
8864
9983
  wasm,
9984
+ windowSize,
8865
9985
  withBoundary,
8866
9986
  withDefaults,
8867
9987
  withErrorTracking,