sibujs 1.2.0 → 1.3.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 (89) 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 +654 -144
  7. package/dist/build.js +14 -12
  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-DAHRH4ON.js +331 -0
  20. package/dist/chunk-EBGIRKQY.js +616 -0
  21. package/dist/chunk-EUZND3CB.js +27 -0
  22. package/dist/chunk-F3FA4F32.js +292 -0
  23. package/dist/chunk-JAKHTMQU.js +1000 -0
  24. package/dist/chunk-JCI5M6U6.js +956 -0
  25. package/dist/chunk-KQPDEVVS.js +398 -0
  26. package/dist/chunk-NEKUBFPT.js +60 -0
  27. package/dist/chunk-NYVAC6P5.js +37 -0
  28. package/dist/chunk-PTQJDMRT.js +146 -0
  29. package/dist/chunk-QWZG56ET.js +2744 -0
  30. package/dist/chunk-TSOKIX5Z.js +654 -0
  31. package/dist/chunk-VRW3FULF.js +725 -0
  32. package/dist/chunk-WZSPOOER.js +84 -0
  33. package/dist/chunk-YT6HQ6AM.js +14 -0
  34. package/dist/chunk-ZD6OAMTH.js +277 -0
  35. package/dist/contracts-DDrwxvJ-.d.cts +245 -0
  36. package/dist/contracts-DDrwxvJ-.d.ts +245 -0
  37. package/dist/data.cjs +35 -2
  38. package/dist/data.d.cts +7 -0
  39. package/dist/data.d.ts +7 -0
  40. package/dist/data.js +9 -8
  41. package/dist/devtools.cjs +122 -0
  42. package/dist/devtools.d.cts +69 -461
  43. package/dist/devtools.d.ts +69 -461
  44. package/dist/devtools.js +127 -6
  45. package/dist/ecosystem.cjs +23 -6
  46. package/dist/ecosystem.d.cts +1 -1
  47. package/dist/ecosystem.d.ts +1 -1
  48. package/dist/ecosystem.js +10 -9
  49. package/dist/extras.cjs +1207 -65
  50. package/dist/extras.d.cts +5 -5
  51. package/dist/extras.d.ts +5 -5
  52. package/dist/extras.js +69 -24
  53. package/dist/index.cjs +663 -144
  54. package/dist/index.d.cts +397 -17
  55. package/dist/index.d.ts +397 -17
  56. package/dist/index.js +39 -17
  57. package/dist/introspect-BumjnBKr.d.cts +477 -0
  58. package/dist/introspect-CZrlcaYy.d.ts +477 -0
  59. package/dist/introspect-Cb0zgpi2.d.cts +477 -0
  60. package/dist/introspect-Y2xNXGSf.d.ts +477 -0
  61. package/dist/motion.js +4 -4
  62. package/dist/patterns.cjs +51 -2
  63. package/dist/patterns.d.cts +18 -8
  64. package/dist/patterns.d.ts +18 -8
  65. package/dist/patterns.js +7 -7
  66. package/dist/performance.js +4 -4
  67. package/dist/plugins.cjs +428 -81
  68. package/dist/plugins.d.cts +27 -4
  69. package/dist/plugins.d.ts +27 -4
  70. package/dist/plugins.js +156 -37
  71. package/dist/ssr-4PBXAOO3.js +40 -0
  72. package/dist/ssr-Do_SiVoL.d.cts +201 -0
  73. package/dist/ssr-Do_SiVoL.d.ts +201 -0
  74. package/dist/ssr.cjs +312 -60
  75. package/dist/ssr.d.cts +10 -1
  76. package/dist/ssr.d.ts +10 -1
  77. package/dist/ssr.js +13 -10
  78. package/dist/tagFactory-DaJ0YWX6.d.cts +47 -0
  79. package/dist/tagFactory-DaJ0YWX6.d.ts +47 -0
  80. package/dist/testing.cjs +233 -2
  81. package/dist/testing.d.cts +42 -1
  82. package/dist/testing.d.ts +42 -1
  83. package/dist/testing.js +129 -2
  84. package/dist/ui.cjs +374 -3
  85. package/dist/ui.d.cts +252 -2
  86. package/dist/ui.d.ts +252 -2
  87. package/dist/ui.js +328 -8
  88. package/dist/widgets.js +7 -7
  89. 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,
@@ -117,10 +120,12 @@ __export(extras_exports, {
117
120
  email: () => email,
118
121
  enableDebug: () => enableDebug,
119
122
  env: () => env,
123
+ escapeScriptJson: () => escapeScriptJson,
120
124
  eventBus: () => eventBus,
121
125
  executeLoader: () => executeLoader,
122
126
  fadeIn: () => fadeIn,
123
127
  fadeOut: () => fadeOut,
128
+ favicon: () => favicon,
124
129
  fileUpload: () => fileUpload,
125
130
  flipIn: () => flipIn,
126
131
  flushScheduler: () => flushScheduler,
@@ -129,6 +134,8 @@ __export(extras_exports, {
129
134
  formatCurrency: () => formatCurrency,
130
135
  formatError: () => formatError,
131
136
  formatNumber: () => formatNumber,
137
+ fullscreen: () => fullscreen,
138
+ gamepad: () => gamepad,
132
139
  generateStaticSite: () => generateStaticSite,
133
140
  geo: () => geo,
134
141
  getActiveDevTools: () => getActiveDevTools,
@@ -147,6 +154,7 @@ __export(extras_exports, {
147
154
  hydrateIslands: () => hydrateIslands,
148
155
  hydrateProgressively: () => hydrateProgressively,
149
156
  idle: () => idle,
157
+ imageLoader: () => imageLoader,
150
158
  infiniteQuery: () => infiniteQuery,
151
159
  infiniteScroll: () => infiniteScroll,
152
160
  initDevTools: () => initDevTools,
@@ -159,6 +167,7 @@ __export(extras_exports, {
159
167
  isHMRAvailable: () => isHMRAvailable,
160
168
  isWasmCached: () => isWasmCached,
161
169
  island: () => island,
170
+ keyboard: () => keyboard,
162
171
  lazyChunk: () => lazyChunk,
163
172
  lazyLoad: () => lazyLoad,
164
173
  lazyModule: () => lazyModule,
@@ -175,7 +184,10 @@ __export(extras_exports, {
175
184
  min: () => min,
176
185
  minLength: () => minLength,
177
186
  mobXAdapter: () => mobXAdapter,
187
+ mouse: () => mouse,
178
188
  mutation: () => mutation,
189
+ mutationObserver: () => mutationObserver,
190
+ network: () => network,
179
191
  noSideEffect: () => noSideEffect,
180
192
  normalize: () => normalize,
181
193
  normalizedStore: () => normalizedStore,
@@ -192,6 +204,7 @@ __export(extras_exports, {
192
204
  persisted: () => persisted,
193
205
  phoneMask: () => phoneMask,
194
206
  plugin: () => plugin,
207
+ pointerLock: () => pointerLock,
195
208
  popover: () => popover,
196
209
  precompile: () => precompile,
197
210
  prefetch: () => prefetch,
@@ -243,6 +256,7 @@ __export(extras_exports, {
243
256
  slideIn: () => slideIn,
244
257
  slideOut: () => slideOut,
245
258
  socket: () => socket,
259
+ speech: () => speech,
246
260
  spring: () => spring,
247
261
  springSignal: () => springSignal,
248
262
  ssnMask: () => ssnMask,
@@ -254,8 +268,11 @@ __export(extras_exports, {
254
268
  stream: () => stream,
255
269
  suspenseSwapScript: () => suspenseSwapScript,
256
270
  svgElement: () => svgElement,
271
+ svgFavicon: () => svgFavicon,
272
+ swipe: () => swipe,
257
273
  syncAdapter: () => syncAdapter,
258
274
  tabs: () => tabs,
275
+ textSelection: () => textSelection,
259
276
  throttle: () => throttle,
260
277
  timeMask: () => timeMask,
261
278
  timeline: () => timeline,
@@ -270,11 +287,16 @@ __export(extras_exports, {
270
287
  triggerPluginUnmount: () => triggerPluginUnmount,
271
288
  trustHTML: () => trustHTML,
272
289
  uniqueId: () => uniqueId,
290
+ urlState: () => urlState,
273
291
  validateProps: () => validateProps,
274
292
  validators: () => validators,
293
+ vibrate: () => vibrate,
275
294
  viewTransition: () => viewTransition,
295
+ visibility: () => visibility,
296
+ wakeLock: () => wakeLock,
276
297
  walkDependencyGraph: () => walkDependencyGraph,
277
298
  wasm: () => wasm,
299
+ windowSize: () => windowSize,
278
300
  withBoundary: () => withBoundary,
279
301
  withDefaults: () => withDefaults,
280
302
  withErrorTracking: () => withErrorTracking,
@@ -1512,6 +1534,13 @@ async function preloadRoute(route, context2) {
1512
1534
  }
1513
1535
 
1514
1536
  // src/ui/socket.ts
1537
+ function validateWsUrl(raw) {
1538
+ const trimmed = raw.replace(/[\x00-\x20\x7f-\x9f]+/g, "").trim();
1539
+ if (!trimmed) return null;
1540
+ const lower = trimmed.toLowerCase();
1541
+ if (lower.startsWith("ws://") || lower.startsWith("wss://")) return trimmed;
1542
+ return null;
1543
+ }
1515
1544
  function socket(url, options) {
1516
1545
  const autoReconnect = options?.autoReconnect ?? false;
1517
1546
  const reconnectDelay = options?.reconnectDelay ?? 1e3;
@@ -1530,8 +1559,13 @@ function socket(url, options) {
1530
1559
  }
1531
1560
  function connect() {
1532
1561
  if (disposed) return;
1562
+ const safeUrl = validateWsUrl(getUrl());
1563
+ if (safeUrl === null) {
1564
+ setStatus("closed");
1565
+ return;
1566
+ }
1533
1567
  setStatus("connecting");
1534
- ws = new WebSocket(getUrl(), protocols);
1568
+ ws = new WebSocket(safeUrl, protocols);
1535
1569
  ws.onopen = () => {
1536
1570
  setStatus("open");
1537
1571
  reconnectCount = 0;
@@ -1592,7 +1626,34 @@ function socket(url, options) {
1592
1626
  return { data: data2, status, send, close, dispose };
1593
1627
  }
1594
1628
 
1629
+ // src/utils/sanitize.ts
1630
+ function sanitizeUrl(url) {
1631
+ const trimmed = url.replace(/[\x00-\x20\x7f-\x9f]+/g, "").trim();
1632
+ if (!trimmed) return "";
1633
+ const lower = trimmed.toLowerCase();
1634
+ if (lower.startsWith("javascript:") || lower.startsWith("data:") || lower.startsWith("vbscript:") || lower.startsWith("blob:")) {
1635
+ return "";
1636
+ }
1637
+ return trimmed;
1638
+ }
1639
+ function sanitizeCSSValue(value) {
1640
+ const lower = value.toLowerCase().replace(/\s+/g, "");
1641
+ if (lower.includes("url(") || lower.includes("expression(") || lower.includes("javascript:") || lower.includes("-moz-binding")) {
1642
+ return "";
1643
+ }
1644
+ return value;
1645
+ }
1646
+ var URL_ATTRIBUTES = /* @__PURE__ */ new Set(["href", "src", "action", "formaction", "cite", "poster", "background", "srcset"]);
1647
+ function isUrlAttribute(attr) {
1648
+ return URL_ATTRIBUTES.has(attr);
1649
+ }
1650
+
1595
1651
  // src/ui/stream.ts
1652
+ function validateSseUrl(raw) {
1653
+ const safe = sanitizeUrl(raw);
1654
+ if (!safe) return null;
1655
+ return safe;
1656
+ }
1596
1657
  function stream(url, options) {
1597
1658
  const autoReconnect = options?.autoReconnect ?? false;
1598
1659
  const [data2, setData] = signal(null);
@@ -1603,8 +1664,13 @@ function stream(url, options) {
1603
1664
  let reconnectTimer = null;
1604
1665
  function connect() {
1605
1666
  if (disposed) return;
1667
+ const safeUrl = validateSseUrl(url);
1668
+ if (safeUrl === null) {
1669
+ setStatus("closed");
1670
+ return;
1671
+ }
1606
1672
  setStatus("connecting");
1607
- source2 = new EventSource(url, {
1673
+ source2 = new EventSource(safeUrl, {
1608
1674
  withCredentials: options?.withCredentials ?? false
1609
1675
  });
1610
1676
  source2.onopen = () => {
@@ -2103,6 +2169,766 @@ function formatCurrency(value, currency, options) {
2103
2169
  }).format(value);
2104
2170
  }
2105
2171
 
2172
+ // src/browser/visibility.ts
2173
+ function visibility() {
2174
+ if (typeof document === "undefined") {
2175
+ const [visible2] = signal(true);
2176
+ return { visible: visible2, dispose: () => {
2177
+ } };
2178
+ }
2179
+ const [visible, setVisible] = signal(!document.hidden);
2180
+ const handler = () => setVisible(!document.hidden);
2181
+ document.addEventListener("visibilitychange", handler);
2182
+ function dispose() {
2183
+ document.removeEventListener("visibilitychange", handler);
2184
+ }
2185
+ return { visible, dispose };
2186
+ }
2187
+
2188
+ // src/browser/network.ts
2189
+ function network() {
2190
+ const connection = typeof navigator !== "undefined" ? navigator.connection ?? navigator.mozConnection ?? navigator.webkitConnection : void 0;
2191
+ const [effectiveType, setEffectiveType] = signal(connection?.effectiveType ?? "unknown");
2192
+ const [downlink, setDownlink] = signal(connection?.downlink ?? 0);
2193
+ const [rtt, setRtt] = signal(connection?.rtt ?? 0);
2194
+ const [saveData, setSaveData] = signal(connection?.saveData ?? false);
2195
+ if (!connection) {
2196
+ return { effectiveType, downlink, rtt, saveData, dispose: () => {
2197
+ } };
2198
+ }
2199
+ const update = () => {
2200
+ setEffectiveType(connection.effectiveType ?? "unknown");
2201
+ setDownlink(connection.downlink ?? 0);
2202
+ setRtt(connection.rtt ?? 0);
2203
+ setSaveData(connection.saveData ?? false);
2204
+ };
2205
+ connection.addEventListener("change", update);
2206
+ function dispose() {
2207
+ connection?.removeEventListener("change", update);
2208
+ }
2209
+ return { effectiveType, downlink, rtt, saveData, dispose };
2210
+ }
2211
+
2212
+ // src/browser/mouse.ts
2213
+ function mouse(options = {}) {
2214
+ const [x, setX] = signal(0);
2215
+ const [y, setY] = signal(0);
2216
+ if (typeof window === "undefined") {
2217
+ return { x, y, dispose: () => {
2218
+ } };
2219
+ }
2220
+ const target = options.target ?? window;
2221
+ const trackTouch = options.touch ?? true;
2222
+ const onMove = (e) => {
2223
+ setX(e.clientX);
2224
+ setY(e.clientY);
2225
+ };
2226
+ const onTouchMove = (e) => {
2227
+ if (e.touches.length === 0) return;
2228
+ setX(e.touches[0].clientX);
2229
+ setY(e.touches[0].clientY);
2230
+ };
2231
+ target.addEventListener("mousemove", onMove, { passive: true });
2232
+ if (trackTouch) {
2233
+ target.addEventListener("touchmove", onTouchMove, { passive: true });
2234
+ }
2235
+ function dispose() {
2236
+ target.removeEventListener("mousemove", onMove);
2237
+ if (trackTouch) {
2238
+ target.removeEventListener("touchmove", onTouchMove);
2239
+ }
2240
+ }
2241
+ return { x, y, dispose };
2242
+ }
2243
+
2244
+ // src/browser/swipe.ts
2245
+ function swipe(target, options = {}) {
2246
+ const threshold = options.threshold ?? 50;
2247
+ const [direction, setDirection] = signal(null);
2248
+ if (typeof window === "undefined") {
2249
+ return { direction, dispose: () => {
2250
+ } };
2251
+ }
2252
+ let startX = 0;
2253
+ let startY = 0;
2254
+ let tracking = false;
2255
+ const onStart = (e) => {
2256
+ if (e.touches.length === 0) return;
2257
+ startX = e.touches[0].clientX;
2258
+ startY = e.touches[0].clientY;
2259
+ tracking = true;
2260
+ };
2261
+ const onEnd = (e) => {
2262
+ if (!tracking) return;
2263
+ tracking = false;
2264
+ const touch = e.changedTouches[0];
2265
+ if (!touch) return;
2266
+ const dx = touch.clientX - startX;
2267
+ const dy = touch.clientY - startY;
2268
+ const absX = Math.abs(dx);
2269
+ const absY = Math.abs(dy);
2270
+ if (Math.max(absX, absY) < threshold) return;
2271
+ let dir;
2272
+ if (absX > absY) {
2273
+ dir = dx > 0 ? "right" : "left";
2274
+ } else {
2275
+ dir = dy > 0 ? "down" : "up";
2276
+ }
2277
+ setDirection(dir);
2278
+ options.onSwipe?.(dir, Math.max(absX, absY));
2279
+ };
2280
+ target.addEventListener("touchstart", onStart, { passive: true });
2281
+ target.addEventListener("touchend", onEnd, { passive: true });
2282
+ function dispose() {
2283
+ target.removeEventListener("touchstart", onStart);
2284
+ target.removeEventListener("touchend", onEnd);
2285
+ }
2286
+ return { direction, dispose };
2287
+ }
2288
+
2289
+ // src/browser/windowSize.ts
2290
+ function windowSize() {
2291
+ if (typeof window === "undefined") {
2292
+ const [width2] = signal(0);
2293
+ const [height2] = signal(0);
2294
+ return { width: width2, height: height2, dispose: () => {
2295
+ } };
2296
+ }
2297
+ const [width, setWidth] = signal(window.innerWidth);
2298
+ const [height, setHeight] = signal(window.innerHeight);
2299
+ const handler = () => {
2300
+ setWidth(window.innerWidth);
2301
+ setHeight(window.innerHeight);
2302
+ };
2303
+ window.addEventListener("resize", handler, { passive: true });
2304
+ function dispose() {
2305
+ window.removeEventListener("resize", handler);
2306
+ }
2307
+ return { width, height, dispose };
2308
+ }
2309
+
2310
+ // src/browser/urlState.ts
2311
+ function urlState() {
2312
+ if (typeof window === "undefined") {
2313
+ const [params2] = signal(new URLSearchParams());
2314
+ const [hash2] = signal("");
2315
+ return {
2316
+ params: params2,
2317
+ hash: hash2,
2318
+ setParams: () => {
2319
+ },
2320
+ setHash: () => {
2321
+ },
2322
+ dispose: () => {
2323
+ }
2324
+ };
2325
+ }
2326
+ const [params, setParamsSignal] = signal(new URLSearchParams(window.location.search));
2327
+ const [hash, setHashSignal] = signal(window.location.hash);
2328
+ const syncFromLocation = () => {
2329
+ setParamsSignal(new URLSearchParams(window.location.search));
2330
+ setHashSignal(window.location.hash);
2331
+ };
2332
+ const onPopState = () => syncFromLocation();
2333
+ window.addEventListener("popstate", onPopState);
2334
+ function setParams(next, opts = {}) {
2335
+ const p2 = next instanceof URLSearchParams ? next : new URLSearchParams(next);
2336
+ const query2 = p2.toString();
2337
+ const newUrl = `${window.location.pathname}${query2 ? `?${query2}` : ""}${window.location.hash}`;
2338
+ if (opts.replace) window.history.replaceState(null, "", newUrl);
2339
+ else window.history.pushState(null, "", newUrl);
2340
+ setParamsSignal(new URLSearchParams(p2));
2341
+ }
2342
+ function setHash(next, opts = {}) {
2343
+ const normalized = next.startsWith("#") ? next : next ? `#${next}` : "";
2344
+ const newUrl = `${window.location.pathname}${window.location.search}${normalized}`;
2345
+ if (opts.replace) window.history.replaceState(null, "", newUrl);
2346
+ else window.history.pushState(null, "", newUrl);
2347
+ setHashSignal(normalized);
2348
+ }
2349
+ function dispose() {
2350
+ window.removeEventListener("popstate", onPopState);
2351
+ }
2352
+ return { params, hash, setParams, setHash, dispose };
2353
+ }
2354
+
2355
+ // src/browser/broadcast.ts
2356
+ function broadcast(channelName) {
2357
+ if (typeof BroadcastChannel === "undefined") {
2358
+ const [last2] = signal(null);
2359
+ return { last: last2, post: () => {
2360
+ }, dispose: () => {
2361
+ } };
2362
+ }
2363
+ const [last, setLast] = signal(null);
2364
+ const channel = new BroadcastChannel(channelName);
2365
+ const handler = (ev) => setLast(ev.data);
2366
+ channel.addEventListener("message", handler);
2367
+ function post(message) {
2368
+ channel.postMessage(message);
2369
+ }
2370
+ function dispose() {
2371
+ channel.removeEventListener("message", handler);
2372
+ channel.close();
2373
+ }
2374
+ return { last, post, dispose };
2375
+ }
2376
+
2377
+ // src/browser/fullscreen.ts
2378
+ function fullscreen() {
2379
+ if (typeof document === "undefined") {
2380
+ const [isFullscreen2] = signal(false);
2381
+ const [element2] = signal(null);
2382
+ return {
2383
+ isFullscreen: isFullscreen2,
2384
+ element: element2,
2385
+ enter: async () => {
2386
+ },
2387
+ exit: async () => {
2388
+ },
2389
+ toggle: async () => {
2390
+ },
2391
+ dispose: () => {
2392
+ }
2393
+ };
2394
+ }
2395
+ const [isFullscreen, setIsFullscreen] = signal(!!document.fullscreenElement);
2396
+ const [element, setElement] = signal(document.fullscreenElement);
2397
+ const handler = () => {
2398
+ setIsFullscreen(!!document.fullscreenElement);
2399
+ setElement(document.fullscreenElement);
2400
+ };
2401
+ document.addEventListener("fullscreenchange", handler);
2402
+ async function enter(el) {
2403
+ if (!document.fullscreenElement && el.requestFullscreen) {
2404
+ await el.requestFullscreen();
2405
+ }
2406
+ }
2407
+ async function exit() {
2408
+ if (document.fullscreenElement && document.exitFullscreen) {
2409
+ await document.exitFullscreen();
2410
+ }
2411
+ }
2412
+ async function toggle(el) {
2413
+ if (document.fullscreenElement) await exit();
2414
+ else await enter(el);
2415
+ }
2416
+ function dispose() {
2417
+ document.removeEventListener("fullscreenchange", handler);
2418
+ }
2419
+ return { isFullscreen, element, enter, exit, toggle, dispose };
2420
+ }
2421
+
2422
+ // src/browser/wakeLock.ts
2423
+ function wakeLock() {
2424
+ const [active, setActive] = signal(false);
2425
+ if (typeof navigator === "undefined" || !("wakeLock" in navigator) || typeof document === "undefined") {
2426
+ return {
2427
+ active,
2428
+ request: async () => {
2429
+ },
2430
+ release: async () => {
2431
+ },
2432
+ dispose: () => {
2433
+ }
2434
+ };
2435
+ }
2436
+ const api = navigator.wakeLock;
2437
+ let sentinel = null;
2438
+ async function request() {
2439
+ try {
2440
+ sentinel = await api.request("screen");
2441
+ setActive(true);
2442
+ sentinel.addEventListener("release", () => {
2443
+ setActive(false);
2444
+ });
2445
+ } catch {
2446
+ setActive(false);
2447
+ }
2448
+ }
2449
+ async function release() {
2450
+ if (sentinel && !sentinel.released) {
2451
+ await sentinel.release();
2452
+ }
2453
+ sentinel = null;
2454
+ setActive(false);
2455
+ }
2456
+ const onVisibility = () => {
2457
+ if (sentinel?.released && !document.hidden) {
2458
+ void request();
2459
+ }
2460
+ };
2461
+ document.addEventListener("visibilitychange", onVisibility);
2462
+ function dispose() {
2463
+ document.removeEventListener("visibilitychange", onVisibility);
2464
+ void release();
2465
+ }
2466
+ return { active, request, release, dispose };
2467
+ }
2468
+
2469
+ // src/browser/animationFrame.ts
2470
+ function animationFrame(options = {}) {
2471
+ const [delta, setDelta] = signal(0);
2472
+ const [elapsed, setElapsed] = signal(0);
2473
+ const [running, setRunning] = signal(false);
2474
+ if (typeof requestAnimationFrame === "undefined") {
2475
+ return {
2476
+ delta,
2477
+ elapsed,
2478
+ running,
2479
+ pause: () => {
2480
+ },
2481
+ resume: () => {
2482
+ },
2483
+ dispose: () => {
2484
+ }
2485
+ };
2486
+ }
2487
+ let id = null;
2488
+ let prev = -1;
2489
+ let start = -1;
2490
+ const minFrameMs = options.fpsLimit ? 1e3 / options.fpsLimit : 0;
2491
+ const step = (now) => {
2492
+ if (start < 0) start = now;
2493
+ const firstTick = prev < 0;
2494
+ const dt2 = firstTick ? 0 : now - prev;
2495
+ if (firstTick || dt2 >= minFrameMs) {
2496
+ setDelta(dt2);
2497
+ setElapsed(now - start);
2498
+ prev = now;
2499
+ }
2500
+ id = requestAnimationFrame(step);
2501
+ };
2502
+ function resume() {
2503
+ if (id !== null) return;
2504
+ setRunning(true);
2505
+ id = requestAnimationFrame(step);
2506
+ }
2507
+ function pause() {
2508
+ if (id !== null) {
2509
+ cancelAnimationFrame(id);
2510
+ id = null;
2511
+ }
2512
+ setRunning(false);
2513
+ prev = -1;
2514
+ start = -1;
2515
+ }
2516
+ function dispose() {
2517
+ pause();
2518
+ }
2519
+ if (options.immediate !== false) resume();
2520
+ return { delta, elapsed, running, pause, resume, dispose };
2521
+ }
2522
+
2523
+ // src/browser/mutationObserver.ts
2524
+ function mutationObserver(target, options = { childList: true, subtree: true }) {
2525
+ const [records, setRecords] = signal([]);
2526
+ if (typeof MutationObserver === "undefined") {
2527
+ return { records, dispose: () => {
2528
+ } };
2529
+ }
2530
+ const observer = new MutationObserver((batch2) => {
2531
+ setRecords(batch2);
2532
+ });
2533
+ observer.observe(target, options);
2534
+ function dispose() {
2535
+ observer.disconnect();
2536
+ }
2537
+ return { records, dispose };
2538
+ }
2539
+
2540
+ // src/browser/bounds.ts
2541
+ var ZERO = {
2542
+ x: 0,
2543
+ y: 0,
2544
+ width: 0,
2545
+ height: 0,
2546
+ top: 0,
2547
+ left: 0,
2548
+ right: 0,
2549
+ bottom: 0
2550
+ };
2551
+ function readRect(el) {
2552
+ const r = el.getBoundingClientRect();
2553
+ return {
2554
+ x: r.x,
2555
+ y: r.y,
2556
+ width: r.width,
2557
+ height: r.height,
2558
+ top: r.top,
2559
+ left: r.left,
2560
+ right: r.right,
2561
+ bottom: r.bottom
2562
+ };
2563
+ }
2564
+ function bounds(target) {
2565
+ const [rect2, setRect] = signal(ZERO);
2566
+ if (typeof window === "undefined" || !target) {
2567
+ return {
2568
+ rect: rect2,
2569
+ refresh: () => {
2570
+ },
2571
+ dispose: () => {
2572
+ }
2573
+ };
2574
+ }
2575
+ function refresh() {
2576
+ setRect(readRect(target));
2577
+ }
2578
+ refresh();
2579
+ let resizeObserver = null;
2580
+ if (typeof ResizeObserver !== "undefined") {
2581
+ resizeObserver = new ResizeObserver(refresh);
2582
+ resizeObserver.observe(target);
2583
+ }
2584
+ const onScroll = () => refresh();
2585
+ window.addEventListener("scroll", onScroll, { passive: true, capture: true });
2586
+ function dispose() {
2587
+ resizeObserver?.disconnect();
2588
+ window.removeEventListener("scroll", onScroll, { capture: true });
2589
+ }
2590
+ return { rect: rect2, refresh, dispose };
2591
+ }
2592
+
2593
+ // src/browser/keyboard.ts
2594
+ function keyboard(options = {}) {
2595
+ const [pressed, setPressed] = signal(/* @__PURE__ */ new Set());
2596
+ if (typeof window === "undefined") {
2597
+ return {
2598
+ pressed,
2599
+ isPressed: () => false,
2600
+ dispose: () => {
2601
+ }
2602
+ };
2603
+ }
2604
+ const target = options.target ?? window;
2605
+ const filter = options.keys ? new Set(options.keys) : null;
2606
+ const onDown = (e) => {
2607
+ if (filter && !filter.has(e.key)) return;
2608
+ setPressed((prev) => {
2609
+ if (prev.has(e.key)) return prev;
2610
+ const next = new Set(prev);
2611
+ next.add(e.key);
2612
+ return next;
2613
+ });
2614
+ };
2615
+ const onUp = (e) => {
2616
+ if (filter && !filter.has(e.key)) return;
2617
+ setPressed((prev) => {
2618
+ if (!prev.has(e.key)) return prev;
2619
+ const next = new Set(prev);
2620
+ next.delete(e.key);
2621
+ return next;
2622
+ });
2623
+ };
2624
+ const onBlur = () => setPressed(/* @__PURE__ */ new Set());
2625
+ target.addEventListener("keydown", onDown);
2626
+ target.addEventListener("keyup", onUp);
2627
+ window.addEventListener("blur", onBlur);
2628
+ function isPressed(key) {
2629
+ return pressed().has(key);
2630
+ }
2631
+ function dispose() {
2632
+ target.removeEventListener("keydown", onDown);
2633
+ target.removeEventListener("keyup", onUp);
2634
+ window.removeEventListener("blur", onBlur);
2635
+ }
2636
+ return { pressed, isPressed, dispose };
2637
+ }
2638
+
2639
+ // src/browser/speech.ts
2640
+ function speech() {
2641
+ const [speaking, setSpeaking] = signal(false);
2642
+ const [paused, setPaused] = signal(false);
2643
+ if (typeof window === "undefined" || typeof window.speechSynthesis === "undefined") {
2644
+ return {
2645
+ speaking,
2646
+ paused,
2647
+ speak: () => {
2648
+ },
2649
+ pause: () => {
2650
+ },
2651
+ resume: () => {
2652
+ },
2653
+ cancel: () => {
2654
+ },
2655
+ dispose: () => {
2656
+ }
2657
+ };
2658
+ }
2659
+ const synth = window.speechSynthesis;
2660
+ const interval = setInterval(() => {
2661
+ setSpeaking(synth.speaking);
2662
+ setPaused(synth.paused);
2663
+ }, 200);
2664
+ function speak(text2, options = {}) {
2665
+ const u2 = new SpeechSynthesisUtterance(text2);
2666
+ if (options.lang) u2.lang = options.lang;
2667
+ if (options.rate != null) u2.rate = options.rate;
2668
+ if (options.pitch != null) u2.pitch = options.pitch;
2669
+ if (options.volume != null) u2.volume = options.volume;
2670
+ if (options.voice) {
2671
+ const voices = synth.getVoices();
2672
+ const match = voices.find((v) => v.name === options.voice);
2673
+ if (match) u2.voice = match;
2674
+ }
2675
+ u2.addEventListener("start", () => setSpeaking(true));
2676
+ u2.addEventListener("end", () => {
2677
+ setSpeaking(false);
2678
+ setPaused(false);
2679
+ });
2680
+ u2.addEventListener("error", () => {
2681
+ setSpeaking(false);
2682
+ setPaused(false);
2683
+ });
2684
+ synth.speak(u2);
2685
+ }
2686
+ function dispose() {
2687
+ clearInterval(interval);
2688
+ synth.cancel();
2689
+ }
2690
+ return {
2691
+ speaking,
2692
+ paused,
2693
+ speak,
2694
+ pause: () => synth.pause(),
2695
+ resume: () => synth.resume(),
2696
+ cancel: () => synth.cancel(),
2697
+ dispose
2698
+ };
2699
+ }
2700
+
2701
+ // src/browser/gamepad.ts
2702
+ function gamepad() {
2703
+ const [pads, setPads] = signal([]);
2704
+ if (typeof window === "undefined" || typeof navigator === "undefined" || typeof navigator.getGamepads !== "function") {
2705
+ return { pads, dispose: () => {
2706
+ } };
2707
+ }
2708
+ let rafId = null;
2709
+ function snapshot(pad) {
2710
+ return {
2711
+ index: pad.index,
2712
+ id: pad.id,
2713
+ connected: pad.connected,
2714
+ buttons: pad.buttons.map((b2) => ({ pressed: b2.pressed, value: b2.value })),
2715
+ axes: [...pad.axes]
2716
+ };
2717
+ }
2718
+ function equal(a2, b2) {
2719
+ if (a2.length !== b2.length) return false;
2720
+ for (let i2 = 0; i2 < a2.length; i2++) {
2721
+ const pa = a2[i2];
2722
+ const pb = b2[i2];
2723
+ if (pa.index !== pb.index || pa.connected !== pb.connected) return false;
2724
+ if (pa.buttons.length !== pb.buttons.length) return false;
2725
+ for (let j = 0; j < pa.buttons.length; j++) {
2726
+ if (pa.buttons[j].pressed !== pb.buttons[j].pressed) return false;
2727
+ if (pa.buttons[j].value !== pb.buttons[j].value) return false;
2728
+ }
2729
+ if (pa.axes.length !== pb.axes.length) return false;
2730
+ for (let j = 0; j < pa.axes.length; j++) {
2731
+ if (pa.axes[j] !== pb.axes[j]) return false;
2732
+ }
2733
+ }
2734
+ return true;
2735
+ }
2736
+ function poll() {
2737
+ const raw = navigator.getGamepads();
2738
+ const snap = Array.from(raw).filter((g2) => g2 !== null).map(snapshot);
2739
+ const current = pads();
2740
+ if (!equal(current, snap)) setPads(snap);
2741
+ rafId = requestAnimationFrame(poll);
2742
+ }
2743
+ function startPolling() {
2744
+ if (rafId === null) poll();
2745
+ }
2746
+ function stopPolling() {
2747
+ if (rafId !== null) {
2748
+ cancelAnimationFrame(rafId);
2749
+ rafId = null;
2750
+ }
2751
+ }
2752
+ const onConnect = () => startPolling();
2753
+ const onDisconnect = () => {
2754
+ const raw = navigator.getGamepads();
2755
+ const hasAny = Array.from(raw).some((g2) => g2 !== null);
2756
+ if (!hasAny) stopPolling();
2757
+ };
2758
+ window.addEventListener("gamepadconnected", onConnect);
2759
+ window.addEventListener("gamepaddisconnected", onDisconnect);
2760
+ const initial = Array.from(navigator.getGamepads()).some((g2) => g2 !== null);
2761
+ if (initial) startPolling();
2762
+ function dispose() {
2763
+ stopPolling();
2764
+ window.removeEventListener("gamepadconnected", onConnect);
2765
+ window.removeEventListener("gamepaddisconnected", onDisconnect);
2766
+ }
2767
+ return { pads, dispose };
2768
+ }
2769
+
2770
+ // src/browser/pointerLock.ts
2771
+ function pointerLock() {
2772
+ const [locked, setLocked] = signal(false);
2773
+ if (typeof document === "undefined") {
2774
+ return {
2775
+ locked,
2776
+ request: () => {
2777
+ },
2778
+ exit: () => {
2779
+ },
2780
+ dispose: () => {
2781
+ }
2782
+ };
2783
+ }
2784
+ const handler = () => {
2785
+ setLocked(!!document.pointerLockElement);
2786
+ };
2787
+ document.addEventListener("pointerlockchange", handler);
2788
+ function request(element) {
2789
+ if (typeof element.requestPointerLock === "function") {
2790
+ element.requestPointerLock();
2791
+ }
2792
+ }
2793
+ function exit() {
2794
+ if (typeof document.exitPointerLock === "function") {
2795
+ document.exitPointerLock();
2796
+ }
2797
+ }
2798
+ function dispose() {
2799
+ document.removeEventListener("pointerlockchange", handler);
2800
+ }
2801
+ return { locked, request, exit, dispose };
2802
+ }
2803
+
2804
+ // src/browser/vibrate.ts
2805
+ function vibrate(pattern2) {
2806
+ if (typeof navigator === "undefined" || typeof navigator.vibrate !== "function") {
2807
+ return false;
2808
+ }
2809
+ return navigator.vibrate(pattern2);
2810
+ }
2811
+
2812
+ // src/browser/favicon.ts
2813
+ function favicon(url) {
2814
+ if (typeof document === "undefined") return;
2815
+ let link2 = document.querySelector("link[rel='icon']");
2816
+ if (!link2) {
2817
+ link2 = document.createElement("link");
2818
+ link2.rel = "icon";
2819
+ document.head.appendChild(link2);
2820
+ }
2821
+ link2.href = url;
2822
+ }
2823
+ function svgFavicon(svg2) {
2824
+ return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg2)}`;
2825
+ }
2826
+
2827
+ // src/browser/textSelection.ts
2828
+ function textSelection() {
2829
+ const [text2, setText] = signal("");
2830
+ const [rect2, setRect] = signal(null);
2831
+ if (typeof document === "undefined") {
2832
+ return {
2833
+ text: text2,
2834
+ rect: rect2,
2835
+ hasSelection: () => false,
2836
+ clear: () => {
2837
+ },
2838
+ dispose: () => {
2839
+ }
2840
+ };
2841
+ }
2842
+ const handler = () => {
2843
+ const sel = document.getSelection();
2844
+ if (!sel || sel.rangeCount === 0 || sel.isCollapsed) {
2845
+ setText("");
2846
+ setRect(null);
2847
+ return;
2848
+ }
2849
+ setText(sel.toString());
2850
+ try {
2851
+ const r = sel.getRangeAt(0).getBoundingClientRect();
2852
+ setRect(r.width > 0 || r.height > 0 ? r : null);
2853
+ } catch {
2854
+ setRect(null);
2855
+ }
2856
+ };
2857
+ document.addEventListener("selectionchange", handler);
2858
+ function clear() {
2859
+ const sel = document.getSelection();
2860
+ sel?.removeAllRanges();
2861
+ setText("");
2862
+ setRect(null);
2863
+ }
2864
+ function dispose() {
2865
+ document.removeEventListener("selectionchange", handler);
2866
+ }
2867
+ return {
2868
+ text: text2,
2869
+ rect: rect2,
2870
+ hasSelection: () => text2().length > 0,
2871
+ clear,
2872
+ dispose
2873
+ };
2874
+ }
2875
+
2876
+ // src/browser/imageLoader.ts
2877
+ function imageLoader(src) {
2878
+ const [status, setStatus] = signal("pending");
2879
+ const [image, setImage] = signal(null);
2880
+ const [width, setWidth] = signal(0);
2881
+ const [height, setHeight] = signal(0);
2882
+ if (typeof Image === "undefined") {
2883
+ return {
2884
+ status,
2885
+ image,
2886
+ width,
2887
+ height,
2888
+ dispose: () => {
2889
+ }
2890
+ };
2891
+ }
2892
+ let current = null;
2893
+ let disposed = false;
2894
+ function start(url) {
2895
+ if (current) {
2896
+ current.onload = null;
2897
+ current.onerror = null;
2898
+ }
2899
+ setStatus("pending");
2900
+ setImage(null);
2901
+ const img2 = new Image();
2902
+ current = img2;
2903
+ img2.onload = () => {
2904
+ if (disposed || current !== img2) return;
2905
+ setImage(img2);
2906
+ setWidth(img2.naturalWidth);
2907
+ setHeight(img2.naturalHeight);
2908
+ setStatus("loaded");
2909
+ };
2910
+ img2.onerror = () => {
2911
+ if (disposed || current !== img2) return;
2912
+ setStatus("error");
2913
+ };
2914
+ img2.src = url;
2915
+ }
2916
+ if (typeof src === "function") {
2917
+ start(src());
2918
+ } else {
2919
+ start(src);
2920
+ }
2921
+ function dispose() {
2922
+ disposed = true;
2923
+ if (current) {
2924
+ current.onload = null;
2925
+ current.onerror = null;
2926
+ current = null;
2927
+ }
2928
+ }
2929
+ return { status, image, width, height, dispose };
2930
+ }
2931
+
2106
2932
  // src/patterns/machine.ts
2107
2933
  function machine(config) {
2108
2934
  const [state, setState] = signal(config.initial);
@@ -2133,8 +2959,13 @@ function machine(config) {
2133
2959
  stateDef.exit(ctx);
2134
2960
  }
2135
2961
  if (action) {
2136
- const patch = action(ctx);
2137
- setContext({ ...ctx, ...patch });
2962
+ const rawPatch = action(ctx);
2963
+ const next = { ...ctx };
2964
+ for (const key of Object.keys(rawPatch)) {
2965
+ if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
2966
+ next[key] = rawPatch[key];
2967
+ }
2968
+ setContext(next);
2138
2969
  }
2139
2970
  setState(target);
2140
2971
  const targetDef = config.states[target];
@@ -2167,6 +2998,7 @@ function persisted(key, initial, options = {}) {
2167
2998
  const deserialize = options.deserialize || JSON.parse;
2168
2999
  const encrypt = options.encrypt;
2169
3000
  const decrypt = options.decrypt;
3001
+ const syncTabs = options.session ? false : options.syncTabs ?? true;
2170
3002
  let restored = initial;
2171
3003
  try {
2172
3004
  let raw = storage.getItem(key);
@@ -2178,8 +3010,10 @@ function persisted(key, initial, options = {}) {
2178
3010
  } catch {
2179
3011
  }
2180
3012
  const [value, setValue] = signal(restored);
3013
+ let applyingFromStorage = false;
2181
3014
  effect(() => {
2182
3015
  const current = value();
3016
+ if (applyingFromStorage) return;
2183
3017
  try {
2184
3018
  let serialized = serialize(current);
2185
3019
  if (encrypt) serialized = encrypt(serialized);
@@ -2187,6 +3021,47 @@ function persisted(key, initial, options = {}) {
2187
3021
  } catch {
2188
3022
  }
2189
3023
  });
3024
+ let storageListener = null;
3025
+ if (syncTabs && typeof window !== "undefined") {
3026
+ storageListener = (e) => {
3027
+ if (e.storageArea !== storage || e.key !== key) return;
3028
+ if (e.newValue === null) {
3029
+ applyingFromStorage = true;
3030
+ try {
3031
+ setValue(initial);
3032
+ } finally {
3033
+ applyingFromStorage = false;
3034
+ }
3035
+ return;
3036
+ }
3037
+ try {
3038
+ let raw = e.newValue;
3039
+ if (decrypt) raw = decrypt(raw);
3040
+ const parsed = deserialize(raw);
3041
+ if (options.validate && !options.validate(parsed)) return;
3042
+ applyingFromStorage = true;
3043
+ try {
3044
+ setValue(parsed);
3045
+ } finally {
3046
+ applyingFromStorage = false;
3047
+ }
3048
+ } catch {
3049
+ }
3050
+ };
3051
+ window.addEventListener("storage", storageListener);
3052
+ }
3053
+ const dispose = () => {
3054
+ if (storageListener && typeof window !== "undefined") {
3055
+ window.removeEventListener("storage", storageListener);
3056
+ storageListener = null;
3057
+ }
3058
+ };
3059
+ Object.defineProperty(setValue, "dispose", {
3060
+ value: dispose,
3061
+ enumerable: false,
3062
+ writable: false,
3063
+ configurable: false
3064
+ });
2190
3065
  return [value, setValue];
2191
3066
  }
2192
3067
 
@@ -3375,8 +4250,25 @@ function announce(message, priority = "polite") {
3375
4250
 
3376
4251
  // src/ui/scopedStyle.ts
3377
4252
  var scopeCounter = 0;
4253
+ function decodeCssEscapes(css) {
4254
+ return css.replace(/\\([0-9a-f]{1,6})[ \t\n\r\f]?|\\([^\n])/gi, (_match, hex, ch) => {
4255
+ if (hex) {
4256
+ const code2 = Number.parseInt(hex, 16);
4257
+ if (Number.isFinite(code2) && code2 > 0 && code2 <= 1114111) {
4258
+ try {
4259
+ return String.fromCodePoint(code2);
4260
+ } catch {
4261
+ return "";
4262
+ }
4263
+ }
4264
+ return "";
4265
+ }
4266
+ return ch || "";
4267
+ });
4268
+ }
3378
4269
  function sanitizeCSS(css) {
3379
- let sanitized = css.replace(/@import\s+[^;]+;/gi, "/* @import removed */");
4270
+ let sanitized = decodeCssEscapes(css);
4271
+ sanitized = sanitized.replace(/@import\s+[^;]+;/gi, "/* @import removed */");
3380
4272
  sanitized = sanitized.replace(/url\s*\(\s*(?:"[^"]*"|'[^']*'|[^)]*)\s*\)/gi, "/* url() removed */");
3381
4273
  sanitized = sanitized.replace(/expression\s*\(\s*(?:"[^"]*"|'[^']*'|[^)]*)\s*\)/gi, "/* expression() removed */");
3382
4274
  sanitized = sanitized.replace(/-moz-binding\s*:[^;]+;/gi, "/* -moz-binding removed */");
@@ -3426,31 +4318,22 @@ function removeScopedStyle(scopeId) {
3426
4318
  if (el) el.remove();
3427
4319
  }
3428
4320
 
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
4321
  // src/reactivity/bindAttribute.ts
3452
4322
  var _isDev4 = isDev();
4323
+ function isEventHandlerAttr(name) {
4324
+ if (name.length < 3) return false;
4325
+ const lower = name.toLowerCase();
4326
+ return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
4327
+ }
3453
4328
  function bindAttribute(el, attr, getter) {
4329
+ if (isEventHandlerAttr(attr)) {
4330
+ if (_isDev4)
4331
+ devWarn(
4332
+ `bindAttribute: refusing to bind event-handler attribute "${attr}". Use on:{ ${attr.slice(2)}: fn } instead.`
4333
+ );
4334
+ return () => {
4335
+ };
4336
+ }
3454
4337
  function commit() {
3455
4338
  let value;
3456
4339
  try {
@@ -4374,11 +5257,34 @@ function preloadModules(urls) {
4374
5257
  }
4375
5258
 
4376
5259
  // src/platform/head.ts
4377
- var HEAD_URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "content"]);
5260
+ var HEAD_URL_ATTRS = /* @__PURE__ */ new Set(["href", "src"]);
4378
5261
  function sanitizeHeadAttr(key, value) {
4379
5262
  if (HEAD_URL_ATTRS.has(key)) return sanitizeUrl(value);
4380
5263
  return value;
4381
5264
  }
5265
+ function isDangerousMetaRefresh(metaProps) {
5266
+ const httpEquiv = metaProps["http-equiv"];
5267
+ if (typeof httpEquiv !== "string") return false;
5268
+ if (httpEquiv.toLowerCase() !== "refresh") return false;
5269
+ const content = metaProps.content;
5270
+ if (typeof content !== "string") return false;
5271
+ const normalized = content.replace(/[\x00-\x20\x7f-\x9f]+/g, "").toLowerCase();
5272
+ return normalized.includes("url=javascript:") || normalized.includes("url=data:") || normalized.includes("url=vbscript:") || normalized.includes("url=blob:");
5273
+ }
5274
+ var SAFE_HEAD_ATTR_NAME = /^[A-Za-z_:][-A-Za-z0-9_.:]*$/;
5275
+ function isEventHandlerAttr2(name) {
5276
+ if (name.length < 3) return false;
5277
+ const lower = name.toLowerCase();
5278
+ return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
5279
+ }
5280
+ function isSafeHeadAttr(name) {
5281
+ if (!SAFE_HEAD_ATTR_NAME.test(name)) return false;
5282
+ if (isEventHandlerAttr2(name)) return false;
5283
+ return true;
5284
+ }
5285
+ function escapeScriptJsonLocal(json) {
5286
+ return json.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
5287
+ }
4382
5288
  function Head(props) {
4383
5289
  const anchor = document.createComment("sibu-head");
4384
5290
  const managedElements = [];
@@ -4405,15 +5311,17 @@ function Head(props) {
4405
5311
  }
4406
5312
  if (props.meta) {
4407
5313
  for (const metaProps of props.meta) {
5314
+ if (isDangerousMetaRefresh(metaProps)) continue;
4408
5315
  const el = document.createElement("meta");
4409
5316
  for (const [key, value] of Object.entries(metaProps)) {
5317
+ if (!isSafeHeadAttr(key)) continue;
4410
5318
  if (typeof value === "function") {
4411
5319
  const cleanupFn = effect(() => {
4412
- el.setAttribute(key, value());
5320
+ el.setAttribute(key, sanitizeHeadAttr(key, value()));
4413
5321
  });
4414
5322
  effectCleanups.push(cleanupFn);
4415
5323
  } else {
4416
- el.setAttribute(key, value);
5324
+ el.setAttribute(key, sanitizeHeadAttr(key, value));
4417
5325
  }
4418
5326
  }
4419
5327
  document.head.appendChild(el);
@@ -4424,6 +5332,7 @@ function Head(props) {
4424
5332
  for (const linkProps of props.link) {
4425
5333
  const el = document.createElement("link");
4426
5334
  for (const [key, value] of Object.entries(linkProps)) {
5335
+ if (!isSafeHeadAttr(key)) continue;
4427
5336
  el.setAttribute(key, sanitizeHeadAttr(key, value));
4428
5337
  }
4429
5338
  document.head.appendChild(el);
@@ -4434,6 +5343,7 @@ function Head(props) {
4434
5343
  for (const scriptProps of props.script) {
4435
5344
  const el = document.createElement("script");
4436
5345
  for (const [key, value] of Object.entries(scriptProps)) {
5346
+ if (!isSafeHeadAttr(key)) continue;
4437
5347
  el.setAttribute(key, sanitizeHeadAttr(key, value));
4438
5348
  }
4439
5349
  document.head.appendChild(el);
@@ -4444,7 +5354,10 @@ function Head(props) {
4444
5354
  const existing = document.head.querySelector("base");
4445
5355
  if (existing) existing.remove();
4446
5356
  const el = document.createElement("base");
4447
- if (props.base.href) el.href = props.base.href;
5357
+ if (props.base.href) {
5358
+ const safeHref = sanitizeUrl(props.base.href);
5359
+ if (safeHref) el.href = safeHref;
5360
+ }
4448
5361
  if (props.base.target) el.target = props.base.target;
4449
5362
  document.head.appendChild(el);
4450
5363
  managedElements.push(el);
@@ -4459,7 +5372,7 @@ function setStructuredData(data2) {
4459
5372
  const script2 = document.createElement("script");
4460
5373
  script2.type = "application/ld+json";
4461
5374
  script2.setAttribute("data-sibu", "true");
4462
- script2.textContent = JSON.stringify(data2);
5375
+ script2.textContent = escapeScriptJsonLocal(JSON.stringify(data2));
4463
5376
  document.head.appendChild(script2);
4464
5377
  }
4465
5378
  function setCanonical(url) {
@@ -4474,12 +5387,39 @@ function setCanonical(url) {
4474
5387
 
4475
5388
  // src/platform/ssr.ts
4476
5389
  var _isDev5 = isDev();
5390
+ var SAFE_ATTR_NAME = /^[A-Za-z_:][-A-Za-z0-9_.:]*$/;
5391
+ function isSafeAttrName(name) {
5392
+ return SAFE_ATTR_NAME.test(name);
5393
+ }
5394
+ function isEventHandlerAttr3(name) {
5395
+ if (name.length < 3) return false;
5396
+ const lower = name.toLowerCase();
5397
+ return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
5398
+ }
5399
+ var URL_ATTRS = /* @__PURE__ */ new Set([
5400
+ "href",
5401
+ "src",
5402
+ "action",
5403
+ "formaction",
5404
+ "cite",
5405
+ "poster",
5406
+ "background",
5407
+ "srcset",
5408
+ "ping",
5409
+ "manifest",
5410
+ "data",
5411
+ "xlink:href"
5412
+ ]);
4477
5413
  function ssrErrorComment(err) {
4478
5414
  if (_isDev5) {
4479
- return `<!--SSR error: ${escapeHtml(err instanceof Error ? err.message : String(err))}-->`;
5415
+ const msg = escapeHtml(err instanceof Error ? err.message : String(err));
5416
+ return `<!--SSR error: ${safeCommentText(msg)}-->`;
4480
5417
  }
4481
5418
  return "<!--SSR error-->";
4482
5419
  }
5420
+ function safeCommentText(text2) {
5421
+ return text2.replace(/-->/g, "--&gt;").replace(/--!>/g, "--!&gt;").replace(/<!--/g, "&lt;!--").replace(/--$/g, "--&#45;");
5422
+ }
4483
5423
  var VOID_ELEMENTS = /* @__PURE__ */ new Set([
4484
5424
  "area",
4485
5425
  "base",
@@ -4510,16 +5450,30 @@ function renderToString(element) {
4510
5450
  return escapeHtml(element.textContent || "");
4511
5451
  }
4512
5452
  if (element.nodeType === 8) {
4513
- const content = (element.textContent || "").replace(/-->/g, "--&gt;");
4514
- return `<!--${content}-->`;
5453
+ return `<!--${safeCommentText(element.textContent || "")}-->`;
4515
5454
  }
4516
5455
  if (!(element instanceof HTMLElement)) {
4517
- return element.textContent || "";
5456
+ return escapeHtml(element.textContent || "");
4518
5457
  }
4519
5458
  const tag = element.tagName.toLowerCase();
5459
+ if (tag === "script" || tag === "style") {
5460
+ return _isDev5 ? `<!--ssr:${tag}-stripped-->` : "";
5461
+ }
5462
+ if (!/^[a-z][a-z0-9-]*$/i.test(tag)) {
5463
+ return _isDev5 ? "<!--ssr:invalid-tag-->" : "";
5464
+ }
4520
5465
  let html2 = `<${tag}`;
4521
5466
  for (const attr of Array.from(element.attributes)) {
4522
- html2 += ` ${attr.name}="${escapeAttr(attr.value)}"`;
5467
+ const rawName = attr.name;
5468
+ if (!isSafeAttrName(rawName)) continue;
5469
+ if (isEventHandlerAttr3(rawName)) continue;
5470
+ const lowerName = rawName.toLowerCase();
5471
+ let value = attr.value;
5472
+ if (URL_ATTRS.has(lowerName)) {
5473
+ value = sanitizeUrl(value);
5474
+ if (!value) continue;
5475
+ }
5476
+ html2 += ` ${rawName}="${escapeAttr(value)}"`;
4523
5477
  }
4524
5478
  if (element.dataset && !element.dataset.sibuHydrate) {
4525
5479
  html2 += ` data-sibu-ssr="true"`;
@@ -4538,8 +5492,25 @@ function renderToString(element) {
4538
5492
  html2 += `</${tag}>`;
4539
5493
  return html2;
4540
5494
  }
4541
- function hydrate(component, container) {
5495
+ function hydrate(component, container, options = {}) {
4542
5496
  const clientTree = component();
5497
+ if (options.diagnostics) {
5498
+ const mismatches = [];
5499
+ collectMismatches(container.firstElementChild, clientTree, "", mismatches);
5500
+ if (mismatches.length > 0) {
5501
+ const first = mismatches[0];
5502
+ if (options.onMismatch) {
5503
+ options.onMismatch(first);
5504
+ } else if (_isDev5) {
5505
+ console.warn(
5506
+ `[Sibu hydration] ${first.message}
5507
+ at ${first.path}
5508
+ server: ${first.serverValue}
5509
+ client: ${first.clientValue}`
5510
+ );
5511
+ }
5512
+ }
5513
+ }
4543
5514
  hydrateNode(container.firstElementChild, clientTree);
4544
5515
  container.setAttribute("data-sibu-hydrated", "true");
4545
5516
  }
@@ -4551,9 +5522,119 @@ function hydrateNode(serverNode, clientNode) {
4551
5522
  hydrateNode(serverChildren[i2], clientChildren[i2]);
4552
5523
  }
4553
5524
  }
5525
+ function collectMismatches(serverNode, clientNode, path2, out, max2 = 5) {
5526
+ if (out.length >= max2) return;
5527
+ const nodePath = path2 || clientNode?.tagName?.toLowerCase() || "(root)";
5528
+ if (!serverNode && clientNode) {
5529
+ out.push({
5530
+ kind: "child-count",
5531
+ path: nodePath,
5532
+ serverValue: "(missing)",
5533
+ clientValue: clientNode.tagName.toLowerCase(),
5534
+ message: "Client rendered a node that the server did not emit."
5535
+ });
5536
+ return;
5537
+ }
5538
+ if (serverNode && !clientNode) {
5539
+ out.push({
5540
+ kind: "child-count",
5541
+ path: nodePath,
5542
+ serverValue: serverNode.tagName.toLowerCase(),
5543
+ clientValue: "(missing)",
5544
+ message: "Server rendered a node that the client did not produce."
5545
+ });
5546
+ return;
5547
+ }
5548
+ if (!serverNode || !clientNode) return;
5549
+ if (serverNode.tagName !== clientNode.tagName) {
5550
+ out.push({
5551
+ kind: "tag",
5552
+ path: nodePath,
5553
+ serverValue: serverNode.tagName.toLowerCase(),
5554
+ clientValue: clientNode.tagName.toLowerCase(),
5555
+ message: "Element tag mismatch \u2014 server and client disagree on the element type."
5556
+ });
5557
+ return;
5558
+ }
5559
+ const skipAttrs = /* @__PURE__ */ new Set(["data-sibu-ssr", "data-sibu-hydrated", "data-sibu-island"]);
5560
+ const serverAttrs = /* @__PURE__ */ new Map();
5561
+ for (const a2 of Array.from(serverNode.attributes)) {
5562
+ if (!skipAttrs.has(a2.name)) serverAttrs.set(a2.name, a2.value);
5563
+ }
5564
+ const clientAttrs = /* @__PURE__ */ new Map();
5565
+ for (const a2 of Array.from(clientNode.attributes)) {
5566
+ if (!skipAttrs.has(a2.name)) clientAttrs.set(a2.name, a2.value);
5567
+ }
5568
+ for (const [name, value] of serverAttrs) {
5569
+ if (out.length >= max2) return;
5570
+ if (!clientAttrs.has(name)) {
5571
+ out.push({
5572
+ kind: "attribute",
5573
+ path: `${nodePath}[${name}]`,
5574
+ serverValue: value,
5575
+ clientValue: "(missing)",
5576
+ message: `Attribute "${name}" present on server but missing on client.`
5577
+ });
5578
+ } else if (clientAttrs.get(name) !== value) {
5579
+ out.push({
5580
+ kind: "attribute",
5581
+ path: `${nodePath}[${name}]`,
5582
+ serverValue: value,
5583
+ clientValue: clientAttrs.get(name) ?? "",
5584
+ message: `Attribute "${name}" differs between server and client.`
5585
+ });
5586
+ }
5587
+ }
5588
+ for (const [name, value] of clientAttrs) {
5589
+ if (out.length >= max2) return;
5590
+ if (!serverAttrs.has(name)) {
5591
+ out.push({
5592
+ kind: "attribute",
5593
+ path: `${nodePath}[${name}]`,
5594
+ serverValue: "(missing)",
5595
+ clientValue: value,
5596
+ message: `Attribute "${name}" present on client but missing on server.`
5597
+ });
5598
+ }
5599
+ }
5600
+ const serverChildren = Array.from(serverNode.children);
5601
+ const clientChildren = Array.from(clientNode.children);
5602
+ const max22 = Math.max(serverChildren.length, clientChildren.length);
5603
+ for (let i2 = 0; i2 < max22; i2++) {
5604
+ if (out.length >= max2) return;
5605
+ const childPath = `${nodePath} > ${clientChildren[i2]?.tagName?.toLowerCase() ?? serverChildren[i2]?.tagName?.toLowerCase() ?? "?"}:nth-child(${i2 + 1})`;
5606
+ collectMismatches(serverChildren[i2] ?? null, clientChildren[i2] ?? null, childPath, out, max2);
5607
+ }
5608
+ }
4554
5609
  function trustHTML(html2) {
4555
5610
  return html2;
4556
5611
  }
5612
+ function buildAttrString(attrs, { allowEventHandlers = false } = {}) {
5613
+ if (!attrs) return "";
5614
+ const out = [];
5615
+ for (const rawKey of Object.keys(attrs)) {
5616
+ if (!Object.hasOwn(attrs, rawKey)) continue;
5617
+ if (!isSafeAttrName(rawKey)) continue;
5618
+ if (!allowEventHandlers && isEventHandlerAttr3(rawKey)) continue;
5619
+ const lowerKey = rawKey.toLowerCase();
5620
+ let value = String(attrs[rawKey]);
5621
+ if (URL_ATTRS.has(lowerKey)) {
5622
+ value = sanitizeUrl(value);
5623
+ if (!value) continue;
5624
+ }
5625
+ out.push(`${rawKey}="${escapeAttr(value)}"`);
5626
+ }
5627
+ return out.join(" ");
5628
+ }
5629
+ function isDangerousMetaRefresh2(metaProps) {
5630
+ const httpEquiv = metaProps["http-equiv"];
5631
+ if (typeof httpEquiv !== "string") return false;
5632
+ if (httpEquiv.toLowerCase() !== "refresh") return false;
5633
+ const content = metaProps.content;
5634
+ if (typeof content !== "string") return false;
5635
+ const normalized = content.replace(/[\x00-\x20\x7f-\x9f]+/g, "").toLowerCase();
5636
+ return normalized.includes("url=javascript:") || normalized.includes("url=data:") || normalized.includes("url=vbscript:") || normalized.includes("url=blob:");
5637
+ }
4557
5638
  function renderToDocument(component, options = {}) {
4558
5639
  let content;
4559
5640
  try {
@@ -4561,14 +5642,22 @@ function renderToDocument(component, options = {}) {
4561
5642
  } catch (err) {
4562
5643
  content = ssrErrorComment(err);
4563
5644
  }
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(" ") : "";
5645
+ const metaTags = (options.meta || []).map((attrs) => {
5646
+ if (isDangerousMetaRefresh2(attrs)) return "";
5647
+ const pairs = buildAttrString(attrs);
5648
+ return pairs ? `<meta ${pairs} />` : "";
5649
+ }).filter(Boolean).join("\n ");
5650
+ const linkTags = (options.links || []).map((attrs) => {
5651
+ const pairs = buildAttrString(attrs);
5652
+ return pairs ? `<link ${pairs} />` : "";
5653
+ }).filter(Boolean).join("\n ");
5654
+ const scriptTags = (options.scripts || []).map((src) => {
5655
+ const safe = sanitizeUrl(String(src));
5656
+ if (!safe) return "";
5657
+ return `<script src="${escapeAttr(safe)}"></script>`;
5658
+ }).filter(Boolean).join("\n ");
5659
+ const bodyAttrPairs = buildAttrString(options.bodyAttrs);
5660
+ const bodyAttrs = bodyAttrPairs ? ` ${bodyAttrPairs}` : "";
4572
5661
  return `<!DOCTYPE html>
4573
5662
  <html>
4574
5663
  <head>
@@ -4601,18 +5690,34 @@ async function* renderToStream(element) {
4601
5690
  return;
4602
5691
  }
4603
5692
  if (element.nodeType === 8) {
4604
- const content = (element.textContent || "").replace(/-->/g, "--&gt;");
4605
- yield `<!--${content}-->`;
5693
+ yield `<!--${safeCommentText(element.textContent || "")}-->`;
4606
5694
  return;
4607
5695
  }
4608
5696
  if (!(element instanceof HTMLElement)) {
4609
- yield element.textContent || "";
5697
+ yield escapeHtml(element.textContent || "");
4610
5698
  return;
4611
5699
  }
4612
5700
  const tag = element.tagName.toLowerCase();
5701
+ if (tag === "script" || tag === "style") {
5702
+ if (_isDev5) yield `<!--ssr:${tag}-stripped-->`;
5703
+ return;
5704
+ }
5705
+ if (!/^[a-z][a-z0-9-]*$/i.test(tag)) {
5706
+ if (_isDev5) yield "<!--ssr:invalid-tag-->";
5707
+ return;
5708
+ }
4613
5709
  let openTag = `<${tag}`;
4614
5710
  for (const attr of Array.from(element.attributes)) {
4615
- openTag += ` ${attr.name}="${escapeAttr(attr.value)}"`;
5711
+ const rawName = attr.name;
5712
+ if (!isSafeAttrName(rawName)) continue;
5713
+ if (isEventHandlerAttr3(rawName)) continue;
5714
+ const lowerName = rawName.toLowerCase();
5715
+ let value = attr.value;
5716
+ if (URL_ATTRS.has(lowerName)) {
5717
+ value = sanitizeUrl(value);
5718
+ if (!value) continue;
5719
+ }
5720
+ openTag += ` ${rawName}="${escapeAttr(value)}"`;
4616
5721
  }
4617
5722
  if (VOID_ELEMENTS.has(tag)) {
4618
5723
  yield `${openTag} />`;
@@ -4660,8 +5765,9 @@ function hydrateIslands(container, islands) {
4660
5765
  const markers = container.querySelectorAll("[data-sibu-island]");
4661
5766
  for (const marker2 of Array.from(markers)) {
4662
5767
  const id = marker2.getAttribute("data-sibu-island") ?? "";
5768
+ if (!Object.hasOwn(islands, id)) continue;
4663
5769
  const factory = islands[id];
4664
- if (!factory) continue;
5770
+ if (typeof factory !== "function") continue;
4665
5771
  const clientTree = factory();
4666
5772
  hydrateNode(marker2, clientTree);
4667
5773
  marker2.setAttribute("data-sibu-hydrated", "true");
@@ -4673,8 +5779,9 @@ function hydrateProgressively(container, islands, options) {
4673
5779
  const cleanups = [];
4674
5780
  for (const marker2 of Array.from(markers)) {
4675
5781
  const id = marker2.getAttribute("data-sibu-island") ?? "";
5782
+ if (!Object.hasOwn(islands, id)) continue;
4676
5783
  const factory = islands[id];
4677
- if (!factory) continue;
5784
+ if (typeof factory !== "function") continue;
4678
5785
  const observer = new IntersectionObserver(
4679
5786
  (entries) => {
4680
5787
  for (const entry of entries) {
@@ -4713,24 +5820,33 @@ function ssrSuspense(props) {
4713
5820
  }));
4714
5821
  return { element: wrapper, promise };
4715
5822
  }
5823
+ var SAFE_SUSPENSE_ID = /^[A-Za-z0-9_-]+$/;
4716
5824
  function suspenseSwapScript(id, nonce) {
4717
- const safeId = id.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/</g, "\\u003c").replace(/>/g, "\\u003e");
5825
+ if (!SAFE_SUSPENSE_ID.test(id)) {
5826
+ throw new Error(
5827
+ `[SibuJS SSR] suspenseSwapScript: id must match [A-Za-z0-9_-]+ (got: ${JSON.stringify(id.slice(0, 32))})`
5828
+ );
5829
+ }
4718
5830
  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>`;
5831
+ 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
5832
  }
4721
- async function* renderToSuspenseStream(element, pendingBoundaries = []) {
5833
+ async function* renderToSuspenseStream(element, pendingBoundaries = [], options) {
4722
5834
  yield* renderToStream(element);
4723
5835
  if (pendingBoundaries.length > 0) {
4724
5836
  const resolved = await Promise.all(pendingBoundaries);
4725
5837
  for (const { id, html: html2 } of resolved) {
4726
- yield `<div hidden id="sibu-resolved-${escapeAttr(id)}">${html2}</div>`;
4727
- yield suspenseSwapScript(id);
5838
+ if (!SAFE_SUSPENSE_ID.test(id)) continue;
5839
+ yield `<div hidden id="sibu-resolved-${id}">${html2}</div>`;
5840
+ yield suspenseSwapScript(id, options?.nonce);
4728
5841
  }
4729
5842
  }
4730
5843
  }
4731
5844
  var SSR_DATA_ATTR = "__SIBU_SSR_DATA__";
5845
+ function escapeScriptJson(json) {
5846
+ return json.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
5847
+ }
4732
5848
  function serializeState(state, nonce) {
4733
- const json = JSON.stringify(state).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026");
5849
+ const json = escapeScriptJson(JSON.stringify(state));
4734
5850
  const nonceAttr = nonce ? ` nonce="${escapeAttr(nonce)}"` : "";
4735
5851
  return `<script${nonceAttr}>window.${SSR_DATA_ATTR}=${json}</script>`;
4736
5852
  }
@@ -4745,7 +5861,7 @@ function escapeHtml(str) {
4745
5861
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4746
5862
  }
4747
5863
  function escapeAttr(str) {
4748
- return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5864
+ return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4749
5865
  }
4750
5866
 
4751
5867
  // src/platform/customElement.ts
@@ -5300,16 +6416,20 @@ function appendChildren(el, nodes) {
5300
6416
  var tagFactory = (tag, ns) => (first, second) => {
5301
6417
  const el = ns ? document.createElementNS(ns, tag) : document.createElement(tag);
5302
6418
  if (first === void 0) return el;
5303
- if (second === void 0 && typeof first === "string") {
6419
+ if (typeof first === "string") {
6420
+ if (second !== void 0) {
6421
+ el.setAttribute("class", first);
6422
+ appendChildren(el, second);
6423
+ return el;
6424
+ }
5304
6425
  el.textContent = first;
5305
6426
  return el;
5306
6427
  }
5307
- if (second !== void 0) {
5308
- el.setAttribute("class", first);
5309
- appendChildren(el, second);
6428
+ if (typeof first === "number") {
6429
+ el.textContent = String(first);
5310
6430
  return el;
5311
6431
  }
5312
- if (Array.isArray(first) || first instanceof Node) {
6432
+ if (Array.isArray(first) || first instanceof Node || typeof first === "function") {
5313
6433
  appendChildren(el, first);
5314
6434
  return el;
5315
6435
  }
@@ -5318,7 +6438,7 @@ var tagFactory = (tag, ns) => (first, second) => {
5318
6438
  if (pClass != null) applyClass(el, pClass);
5319
6439
  const pId = props.id;
5320
6440
  if (pId != null) el.id = pId;
5321
- const pNodes = props.nodes;
6441
+ const pNodes = second !== void 0 ? second : props.nodes;
5322
6442
  if (pNodes != null) appendChildren(el, pNodes);
5323
6443
  const pOn = props.on;
5324
6444
  if (pOn) {
@@ -8619,6 +9739,7 @@ var materialAdapter = componentAdapter(materialConfig);
8619
9739
  VirtualList,
8620
9740
  accordion,
8621
9741
  animate,
9742
+ animationFrame,
8622
9743
  announce,
8623
9744
  antdAdapter,
8624
9745
  aria,
@@ -8631,6 +9752,8 @@ var materialAdapter = componentAdapter(materialConfig);
8631
9752
  block,
8632
9753
  bounceIn,
8633
9754
  bounceOut,
9755
+ bounds,
9756
+ broadcast,
8634
9757
  bundlerMetadata,
8635
9758
  calculateDelay,
8636
9759
  chakraAdapter,
@@ -8704,10 +9827,12 @@ var materialAdapter = componentAdapter(materialConfig);
8704
9827
  email,
8705
9828
  enableDebug,
8706
9829
  env,
9830
+ escapeScriptJson,
8707
9831
  eventBus,
8708
9832
  executeLoader,
8709
9833
  fadeIn,
8710
9834
  fadeOut,
9835
+ favicon,
8711
9836
  fileUpload,
8712
9837
  flipIn,
8713
9838
  flushScheduler,
@@ -8716,6 +9841,8 @@ var materialAdapter = componentAdapter(materialConfig);
8716
9841
  formatCurrency,
8717
9842
  formatError,
8718
9843
  formatNumber,
9844
+ fullscreen,
9845
+ gamepad,
8719
9846
  generateStaticSite,
8720
9847
  geo,
8721
9848
  getActiveDevTools,
@@ -8734,6 +9861,7 @@ var materialAdapter = componentAdapter(materialConfig);
8734
9861
  hydrateIslands,
8735
9862
  hydrateProgressively,
8736
9863
  idle,
9864
+ imageLoader,
8737
9865
  infiniteQuery,
8738
9866
  infiniteScroll,
8739
9867
  initDevTools,
@@ -8746,6 +9874,7 @@ var materialAdapter = componentAdapter(materialConfig);
8746
9874
  isHMRAvailable,
8747
9875
  isWasmCached,
8748
9876
  island,
9877
+ keyboard,
8749
9878
  lazyChunk,
8750
9879
  lazyLoad,
8751
9880
  lazyModule,
@@ -8762,7 +9891,10 @@ var materialAdapter = componentAdapter(materialConfig);
8762
9891
  min,
8763
9892
  minLength,
8764
9893
  mobXAdapter,
9894
+ mouse,
8765
9895
  mutation,
9896
+ mutationObserver,
9897
+ network,
8766
9898
  noSideEffect,
8767
9899
  normalize,
8768
9900
  normalizedStore,
@@ -8779,6 +9911,7 @@ var materialAdapter = componentAdapter(materialConfig);
8779
9911
  persisted,
8780
9912
  phoneMask,
8781
9913
  plugin,
9914
+ pointerLock,
8782
9915
  popover,
8783
9916
  precompile,
8784
9917
  prefetch,
@@ -8830,6 +9963,7 @@ var materialAdapter = componentAdapter(materialConfig);
8830
9963
  slideIn,
8831
9964
  slideOut,
8832
9965
  socket,
9966
+ speech,
8833
9967
  spring,
8834
9968
  springSignal,
8835
9969
  ssnMask,
@@ -8841,8 +9975,11 @@ var materialAdapter = componentAdapter(materialConfig);
8841
9975
  stream,
8842
9976
  suspenseSwapScript,
8843
9977
  svgElement,
9978
+ svgFavicon,
9979
+ swipe,
8844
9980
  syncAdapter,
8845
9981
  tabs,
9982
+ textSelection,
8846
9983
  throttle,
8847
9984
  timeMask,
8848
9985
  timeline,
@@ -8857,11 +9994,16 @@ var materialAdapter = componentAdapter(materialConfig);
8857
9994
  triggerPluginUnmount,
8858
9995
  trustHTML,
8859
9996
  uniqueId,
9997
+ urlState,
8860
9998
  validateProps,
8861
9999
  validators,
10000
+ vibrate,
8862
10001
  viewTransition,
10002
+ visibility,
10003
+ wakeLock,
8863
10004
  walkDependencyGraph,
8864
10005
  wasm,
10006
+ windowSize,
8865
10007
  withBoundary,
8866
10008
  withDefaults,
8867
10009
  withErrorTracking,