diff --git a/CHANGELOG.md b/CHANGELOG.md index 2825a9a..aede5bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,172 @@ This project follows [Semantic Versioning](https://semver.org/). --- +## [1.3.0] — 2026-04-11 + +Large minor release. Adds **27 new reactive/DOM primitives**, a full **SSR + OWASP security hardening pass** (A01, A02, A03, A10 + CWE-1321 prototype pollution), **10 ergonomic features** that stay inside the SibuJS philosophy (No VDOM, No JSX, No compilation, Zero dependencies, fine-grained reactivity), **typed tag factory overloads** for common elements, and a new **`tag(props, children)` positional shorthand** that removes the need for the `nodes:` key at every level of the tree. Test suite grew from **1875 → 2113** passing tests (+238, **0 regressions**). + +### Added + +#### Browser composables (`sibujs/browser`) — 20 new primitives + +- **`visibility()`** — Page Visibility API wrapper. Pause polling / animations while the tab is hidden. +- **`network()`** — Network Information API reactive getters (`effectiveType`, `downlink`, `rtt`, `saveData`). Adapt image quality and prefetching to the real connection, not just online/offline. +- **`mouse({ target?, touch? })`** — reactive pointer position with optional touch unification. +- **`swipe(target, { threshold?, onSwipe? })`** — touch swipe detection with configurable threshold and direction callback. +- **`windowSize()`** — reactive viewport dimensions via the `resize` event (complements the element-scoped `resize()`). +- **`urlState()`** — reactive URL search params + hash with `setParams` / `setHash` backed by `history.pushState`/`replaceState` and `popstate` sync. Independent of `createRouter()`. +- **`broadcast(channelName)`** — BroadcastChannel wrapper exposing a reactive `last` signal and a `post(message)` sender. +- **`fullscreen()`** — Fullscreen API with reactive `isFullscreen` / `element` plus `enter` / `exit` / `toggle`. +- **`wakeLock()`** — Screen Wake Lock API with auto re-acquire on `visibilitychange`. +- **`animationFrame({ fpsLimit?, immediate? })`** — reactive `delta` / `elapsed` driven by `requestAnimationFrame`, with `pause` / `resume` / `dispose` and optional FPS limit. +- **`mutationObserver(target, options)`** — reactive DOM MutationObserver wrapper. Escape hatch for reacting to DOM changes outside the reactive system. +- **`bounds(target)`** — reactive `getBoundingClientRect()`. Updates on resize (ResizeObserver) AND on window scroll (capture-phase passive listener), so absolute top/left stay accurate for overlays. +- **`keyboard({ target?, keys? })`** — reactive set of currently-pressed keys with optional filter. Clears on `window.blur` to avoid stuck modifiers. +- **`speech()`** — Web Speech Synthesis wrapper with reactive `speaking` / `paused` and `speak(text, options)` supporting rate / pitch / volume / voice / lang. +- **`gamepad()`** — Gamepad API as reactive snapshots. Auto-polls via `requestAnimationFrame` only when at least one pad is connected, and emits updates only when button or axis state actually changes (deep equality short-circuit). +- **`pointerLock()`** — Pointer Lock API with reactive `locked` signal and `request(el)` / `exit()`. +- **`vibrate(pattern)`** — thin Vibration API wrapper; returns `false` on unsupported platforms. +- **`favicon(url)` / `svgFavicon(svg)`** — runtime favicon updater. Creates the `` if missing; `svgFavicon` encodes inline SVG to a data URI for notification-count badges. +- **`textSelection()`** — reactive text-selection tracker (`text`, `rect`, `hasSelection`, `clear`) for building selection toolbars and citation tools. Syncs via `selectionchange` (mouse drag, Shift+arrow, touch select). +- **`imageLoader(src)`** — reactive image-load status (`"pending"` | `"loaded"` | `"error"`) plus intrinsic `width` / `height`. Prevents CLS in lazy galleries. Gracefully aborts in-flight loads on `dispose()`. + +#### Reactivity / core primitives + +- **`defer(getter)`** — deferred mirror of a reactive getter. Converges to the source on a microtask + `requestAnimationFrame` so expensive derived views lag behind fast input. +- **`transition()`** — `{ pending, start }` handle that schedules work on `requestIdleCallback` (with rAF / setTimeout fallback). `pending()` stays reactive for both sync and async bodies; exceptions reset the state cleanly. +- **`nextTick()`** — await for DOM flush. Resolves on microtask + rAF so imperative code can read post-render state. +- **`asyncDerived(factory, initial)`** — async counterpart of `derived()`. Reactive `value` / `loading` / `error` triple with stale-response cancellation and a `refresh()` trigger. +- **`createId(prefix?)`** — stable unique id generator for a11y pairing (`aria-labelledby`, `for` + `id`). Exports `__resetIdCounter()` for deterministic tests and SSR. +- **`strict(fn)` / `strictEffect(fn)`** — dev-only double-invocation helpers that surface cleanup bugs (missing disposers, duplicate listeners). No-op in production. +- **`escapeScriptJson(json)`** — exported helper used internally by `serializeState` / `serializeRouteState` / `setStructuredData`. Escapes `<`, `>`, `&`, `U+2028`, `U+2029`. + +#### UI helpers (`sibujs/ui`) + +- **`interval(fn, ms)`** — declarative `setInterval` handle with `stop` / `pause` / `resume` / `isRunning`. +- **`timeout(fn, ms)`** — declarative `setTimeout` handle with `cancel` / `isPending`. +- **`hover(target)`** — reactive hover tracker using `pointerenter` / `pointerleave` (touch-friendly). +- **`scrollLock()`** — stacked body scroll lock that compensates for scrollbar width. Multiple concurrent overlays each own a handle; only the last `unlock()` restores the original styles. +- **`formAction(fn)`** — async form-action wrapper: reactive `pending` / `error` / `result` / `reset` / `onSubmit`. `onSubmit` is a ready-to-attach `