Skip to content

Releases: bQuery/bQuery

Release 1.16.1

Choose a tag to compare

@JosunLP JosunLP released this 26 Aug 09:52
b1c4c66

[1.16.1] - 2026-08-26

A toolchain-and-build maintenance patch. Nothing under src/ changed, so every 1.16.0 API behaves identically and upgrading is a drop-in. The supported Bun floor moves to 1.4.0, the dev-dependency set is refreshed, and both Vite configs now build warning-free.

Changed (1.16.1)

  • Toolchain: The supported Bun floor moves from 1.3.13 to 1.4.0 (engines.bun), mirrored in mise.toml, the AI guidance files (AGENT.md, llms.txt, .github/copilot-instructions.md, .cursorrules, .clinerules), the runtime support matrix, and the bug-report template. CI workflows install bun-version: 'latest' instead of pinning a patch release, and the SSR cross-runtime matrix leg bun-1.3 becomes bun-1.4. Node.js stays at >=24.0.0.
  • Build: vite.config.ts and vite.umd.config.ts resolve the repository root from import.meta.dirname instead of __dirname, making both configs compatible with Vite's configLoader: 'native' (planned to become the default in a future major) and silencing the loader warning it emitted on every build.
  • Dev dependencies: Bumped @storybook/addon-docs, @storybook/web-components-vite, and storybook from 10.5.7 to 10.5.10, @typescript-eslint/eslint-plugin and @typescript-eslint/parser from 8.67.0 to 8.68.0, bun-types from 1.3.14 to 1.4.0, eslint from 10.8.1 to 10.9.1, globals from 17.9.0 to 17.11.0, happy-dom from 20.11.2 to 20.11.6, and vite from 8.2.1 to 8.2.2.

Fixed (1.16.1)

  • Build: The UMD/IIFE build no longer logs Module "node:http" has been externalized for browser compatibility. createServer().listen() dynamically imports node:http on its Node branch, which is unreachable in a browser bundle, but Vite's resolver substituted its own stub and warned on every build (rollupOptions.external does not apply — node:* is handled earlier by Vite's client-environment resolver). A build-only plugin in vite.umd.config.ts now maps node:* to a stub module that throws a message naming the missing built-in, so the dynamic import rejects with actionable text instead of failing later as a cryptic "not a function". Bundle contents are otherwise unchanged.

Full Changelog: v1.16.0...v1.16.1

Release 1.16.0

Choose a tag to compare

@JosunLP JosunLP released this 11 Aug 12:26
6eb76be

[1.16.0] - 2026-08-11

A quality-and-performance pass over the three hot paths of the framework — the reactive core, the DOM core, and the view layer — driven by a full audit of each. Signal writes, computed propagation, list reconciliation, and directive updates all got measurably cheaper, and the audit surfaced (and this release fixes) several real correctness bugs. The release also folds in the previously staged follow-up to the 1.15.1 security review (a residual evaluator-hardening gap, the deepClone prototype-pollution guard, and a compiler numeric-literal fix). No breaking changes; one small additive API (watchThrottle's trailing option).

Added (1.16.0)

  • @bquery/bquery/reactivewatchThrottle accepts a new trailing option (WatchThrottleOptions). When true, the last value of a burst is delivered once the interval elapses, so consumers never end up on a stale intermediate value. Defaults to false, preserving the leading-edge-only behavior of earlier releases.

Changed (1.16.0)

  • @bquery/bquery/reactive — batching now spans the whole propagation: batch() keeps the batch open while flushing, so signal writes performed by observers keep coalescing into the same flush instead of dispatching synchronously one by one. Flushes drain re-queued observers in follow-up passes (bounded at 100 passes, mirroring the existing cyclic-effect guard) — diamond dependencies inside a batch now trigger their effect once instead of once per branch.
  • @bquery/bquery/reactiveComputed re-validates before waking subscribers: when a dependency changes but the recomputed value is Object.is-equal to the last observed one, downstream effects are not notified at all. In the micro-benchmark, a computed(() => count.value > 5) under 20k writes went from 20k effect runs to 2.
  • @bquery/bquery/reactive — hot-path allocation cuts: signal writes with zero or one subscriber no longer allocate a snapshot array (~6× faster with no subscribers), repeat reads of the same source inside one observer skip the dependency bookkeeping (~1.6× faster), computed chains propagate ~1.7× faster, and effect() no longer allocates an inspection Symbol when effect inspection is disabled.
  • @bquery/bquery/core — collection/element cheapening: replaceWith(string) sanitizes and parses the HTML once and clones per element (matching insertAll); css(object) hoists Object.entries out of the per-element loop; children()/siblings() iterate live HTMLCollections without Array.from copies and siblings() visits each unique parent once; index() counts previousElementSibling instead of materializing the sibling list; empty() uses replaceChildren() (no HTML parser, no Trusted Types sink); unwrap() collapses to a single replaceWith(...childNodes) mutation; form serialization resolves each control's kind with one tagName.toLowerCase() instead of three; data()'s camel→kebab regex is compiled once at module level. The per-instance delegation maps are gone entirely (see the undelegate fix below).
  • @bquery/bquery/view — per-update work moved to bind time: bq-class/bq-style/bq-aria parse their static object expression once (memoized) instead of on every reactive tick, and pre-normalize property names; bq-if resolves its transition config only on an actual visibility flip instead of on every effect run; bq-text/bq-bind/bq-model skip the DOM write when the value is unchanged; bq-html skips sanitize+parse when the HTML string is unchanged. processElement reuses one set of per-prefix attribute-name strings instead of rebuilding them per element, parseDirective results are memoized (bounded like the expression caches), bq-for's key extraction reuses one context object per reconcile instead of spreading the context per item, and expression evaluation caches its sandbox proxies per context object instead of allocating one per evaluation.
  • Tooling / Dev dependencies: Bumped @storybook/addon-docs and @storybook/web-components-vite from 10.4.6 to 10.5.7, @typescript-eslint/eslint-plugin and @typescript-eslint/parser from 8.63.0 to 8.67.0, eslint from 10.6.0 to 10.8.1, globals from 17.7.0 to 17.9.0, happy-dom from 20.10.6 to 20.11.2, prettier from 3.9.4 to 3.9.6, storybook from 10.4.6 to 10.5.7, and vite from 8.1.3 to 8.2.1.

Fixed (1.16.0)

  • @bquery/bquery/coreundelegate() called on a fresh wrapper (e.g. $$('.container').undelegate(...) after delegating via an earlier $$() call — the documented usage) was a silent no-op because the handler registry lived on the wrapper instance, permanently leaking the delegated listener. The registry is now module-level and keyed by element, delegate() attaches a single listener per (element, event, selector, handler) and counts its registrations so one owner's undelegate() cannot detach a delegation another owner still holds, and the delegated dispatcher no longer throws when event.target is not an Element (e.g. a Text node).
  • @bquery/bquery/corewrap(element) over a multi-element collection cloned the wrapper after the first element had been moved into it, so later wrappers contained copies of previously wrapped elements. The pristine wrapper is snapshotted before the loop.
  • @bquery/bquery/view — directives declared before bq-for on the same element (<li bq-text="item.name" bq-for="item in items">) were bound against the discarded template element and the outer context, leaking a live effect that errored on every update. bq-for is now dispatched first regardless of attribute order.
  • @bquery/bquery/viewbq-once/bq-memo/bq-init evaluated their expression while the enclosing bq-for reconciler was the active observer, silently subscribing the whole list to signals the "non-reactive" directives read. Their evaluation is now untracked, matching their documented contract.
  • @bquery/bquery/viewbq-model re-wrote input.value on the effect tick triggered by the input's own input event, resetting the caret position while typing. The write is now skipped when the input already holds the value.
  • @bquery/bquery/view — children of bq-html/bq-html-safe content were processed for directives at mount and their effects kept running (and writing) after the first re-render replaced the markup. Child processing is skipped for author-opaque HTML content.
  • @bquery/bquery/motiononReducedMotionChange now re-binds to the current window.matchMedia when subscribing (a replaced matchMedia — e.g. in tests or embedded contexts — previously left the subscription attached to the stale source) and flushes preference changes that happened without a change event, so existing listeners and the new subscriber's baseline stay accurate. This also fixes two order-dependent test failures in the motion suite.
  • @bquery/bquery/reactive — nested batch() calls could execute observers twice per flush (the flush loop iterated a stale snapshot); a computed whose compute function threw was left marked clean and served its stale cached value on subsequent reads (it now stays dirty and retries); watchThrottle cancels a pending trailing delivery on scope disposal, mirroring watchDebounce.
  • @bquery/bquery/storedeepClone (used by $patchDeep) now special-cases only the genuinely dangerous __proto__ key, defining it as a real own data property so it can no longer trigger the prototype-reassigning setter. Own data properties merely named constructor or prototype are copied normally again instead of being silently dropped, which had discarded legitimate cloned data.
  • @bquery/bquery/view/compilerNUMERIC_LITERAL_RE now rejects legacy leading-zero decimal literals (007, 01.5), which are SyntaxErrors in the strict-mode ES module the compiler emits, instead of compiling them into invalid output.

Security (1.16.0)

  • @bquery/bquery/view — closes a residual escape from the with-scoped evaluator hardening shipped in 1.15.1 (#168): shadowing dangerous identifiers on the with scope didn't stop a member access chain off any reachable context value from reaching Function, e.g. items.constructor.constructor('return 2')(). A new shared guard, hasDangerousMemberAccess(), rejects dotted (.constructor), optional-chaining (?.constructor), and string-literal bracket (['constructor']) access to constructor, prototype, or __proto__ — applied to both the runtime evaluator (evaluate/evaluateRaw, which now refuse and log instead of executing) and the ahead-of-time compiler (which bails to the runtime evaluator, itself also guarded). Computed bracket access assembled at runtime (foo['con' + 'structor']) remains out of scope, documented as a residual limit of the with-scope evaluator's threat model (#202).

Full Changelog: v1.15.1...v1.16.0

Release 1.15.1

Choose a tag to compare

@JosunLP JosunLP released this 06 Jul 22:31
d6b8e30

[1.15.1] - 2026-07-06

A security-and-correctness patch closing the findings of a full-codebase audit. No breaking changes and no module status transitions — every entry is a fix on the 1.15.0 surface. Three small, backwards-compatible additions are noted inline with the fixes that introduced them (the trustedHtmlForSink helper, the effectScope(detached) parameter, and a dispose() method on deferred()'s handle).

Security (1.15.1)

  • @bquery/bquery/security — the anti-mutation-XSS fallback in sanitizeHtml returned raw, un-escaped textContent when the serialize→re-parse stability check failed. Because every HTML sink ($el.html(), .append()/.before()/.after(), the default-sanitized bq-html) assigns that result to innerHTML, an entity-encoded payload combined with a foster-parenting construct could smuggle live markup through the defense meant to stop it. The fallback is now HTML-escaped (#162).
  • @bquery/bquery/ssrbq-text on raw-text elements (textarea, title) is now escaped in the default DOM-free renderer. Raw-text children are serialized verbatim, so an untrusted value such as </textarea><img onerror=…> could break out of the element (stored XSS) — the escaping now mirrors the existing bq-model handling (#163).
  • @bquery/bquery/view + @bquery/bquery/ssrbq-bind now guards runtime-bound attribute values via a shared src/security/bind-guard.ts: inline on* handlers are never written, URL attributes (href, src, xlink:href, formaction, action, poster, background, cite, data) and srcset reject dangerous protocols, and srcdoc is treated as an HTML sink (sanitized). Applied consistently to the client directive and both SSR backends (#164).
  • @bquery/bquery/view — the with-scoped runtime evaluator no longer resolves inherited members or globals, closing a constructor.constructor('…')() (and bare Function('…')()) code-execution path. The proxy now shadows a denylist (constructor, __proto__, prototype, Function, eval, globalThis, window, self, …) for both evaluate and evaluateRaw; own context properties, arithmetic, and method calls on values are unaffected (#168).
  • @bquery/bquery/ssr — the DOM-backed evaluator now routes through the CSP-safe Pratt parser shared with the pure renderer, removing the new Function() fallback ('unsafe-eval') and a prototype-lookup gap (constructor.constructor reachability). Evaluator behaviour is now unified across both SSR backends (#167).
  • @bquery/bquery/server — the session-id cookie and the CSRF secret cookie now default to Secure, keeping these bearer credentials off plaintext HTTP. Opt out explicitly with cookie: { secure: false } for local HTTP dev (#169).
  • @bquery/bquery/security — Trusted Types are now wired into the framework's HTML sinks. The new trustedHtmlForSink() helper (also re-exported from /full) returns a TrustedHTML object when a policy is active — so writes satisfy an enforced require-trusted-types-for 'script' CSP instead of throwing — and the sanitized string otherwise. setHtml, Collection.html()/insert paths, bq-html, and bq-html-safe route through it (#171).
  • @bquery/bquery/ssrbq-style declarations are validated in the pure renderer before concatenation: property names must be valid CSS identifiers and values containing ;, {, }, or < are dropped, preventing injection of extra declarations/rules (UI-redress, exfiltration) from untrusted style objects (#176).
  • @bquery/bquery/security — DOM-clobbering defenses strengthened: the reserved-id/name denylist is expanded with the many missing high-value targets (attributes, nodeName, getElementById, defaultView, implementation, DOM-traversal properties, …) and duplicate ids within a sanitized fragment are now stripped, mitigating the classic HTMLCollection-clobbering vector. Documented as defense-in-depth (#179).
  • @bquery/bquery/i18n — placeholder and message-key resolution now use own-property checks, so a placeholder or key colliding with an Object.prototype member (toString, constructor, …) is left intact rather than substituted with the inherited value (#174).
  • @bquery/bquery/storedeepClone (used by $patchDeep) now skips prototype-pollution keys, so an own enumerable __proto__ (e.g. from JSON.parse) no longer triggers the setter and reassigns the clone's prototype (#175).
  • @bquery/bquery/server — file-route loader (JSON) endpoints now default their middleware to the action middleware chain. Protecting mutations with middlewares: [auth] no longer accidentally exposes every route's load() output as unauthenticated JSON; opt out with an explicit dataMiddlewares: [] (#181).

Fixed (1.15.1)

  • @bquery/bquery/store$subscribe notifications iterate a snapshot of the subscriber list, so a callback that unsubscribes during notification no longer causes the next subscriber to be silently skipped (mirrors the existing $onAction guard) (#165).
  • @bquery/bquery/reactive — an effect that writes a signal it also reads no longer recurses synchronously into a stack overflow. Self-triggered re-runs are drained in a bounded loop and a cyclic effect update detected warning is logged instead of crashing the page; effects that legitimately settle still converge silently (#166).
  • @bquery/bquery/view/compiler — the compiler bails to the runtime evaluator on an unterminated string literal or an invalid numeric literal instead of emitting a syntactically broken module (one bad expression previously took down every precompiled expression in the emitted file) (#170).
  • @bquery/bquery/reactive — overlapping useFetch / useAsyncData executions (e.g. a watch refresh racing a manual refresh()) now abort the superseded in-flight request instead of leaving it running un-cancellable (#172).
  • @bquery/bquery/reactive + @bquery/bquery/concurrency — composables that created long-lived reactive primitives now have disposal paths. deferred() returns a handle with a dispose(); persistedSignal() runs its persistence effect in a detached scope tied to the signal's own dispose() (so an ambient scope.stop() no longer silently stops persistence). Adds an optional effectScope(detached?) parameter (#173).
  • @bquery/bquery/ssrtitleTemplate inserts the page title literally, so special String.prototype.replace patterns ($&, $1, $`, $') in a title no longer mangle the rendered <title> (#177).
  • @bquery/bquery/coredebounce({ leading: true, trailing: true }) no longer double-invokes on a single call; the trailing edge fires only when the function was called more than once during the wait window (lodash semantics) (#178).
  • @bquery/bquery/viewbq-on decides bare-reference vs. call by evaluating the expression rather than string-scanning for (. Handlers resolved through an expression containing an inner paren (e.g. items.find(fn).handler) are now invoked instead of silently doing nothing (#180).

Full Changelog: v1.15.0...v1.15.1

Version 1.15.0

Choose a tag to compare

@JosunLP JosunLP released this 30 Jun 15:54
b43c847

[1.15.0] - 2026-06-30

This release graduates the final thirteen modules to Stableview, forms, i18n, a11y, dnd, media, plugin, devtools, testing, storybook, concurrency, ssr, and server. With them, every bQuery module is now Stable and bound by the no-breaking-changes-between-minor-releases contract (see STABILITY.md). All graduations are additive — there are no breaking changes this cycle.

Added (1.15.0)

  • @bquery/bquery/view — declarative enter/leave/move transitions (#137). New companion attributes bq-transition, bq-in, bq-out, bq-transition-duration, bq-transition-easing drive enter/leave animations on bq-if / bq-show, and bq-animate="flip" drives FLIP move animations when bq-for items reorder. The layer delegates to the existing motion engine (Web Animations + FLIP), skips the initial paint, defers removal until the leave finishes, is race-safe on rapid toggles, and honours prefers-reduced-motion.
  • @bquery/bquery/view/compiler — optional, build-tool-agnostic compiler (#138). compileViews(), compileToModule(), compileExpression(), emitModule(), and the dependency-free CLI (runCompileCli / compileFiles, bquery-view-compile) pre-parse bq-* expressions into optimized, with-free update functions. New runtime hooks registerCompiledExpressions() / clearCompiledExpressions() (exported from @bquery/bquery/view) let the runtime use the precompiled functions, skipping the new Function() evaluator (and its 'unsafe-eval' requirement). The runtime evaluator stays the default; un-compilable expressions transparently fall back to it, so both paths are behaviourally identical.
  • @bquery/bquery/forms — progressive-enhancement form actions + optimistic updates (#140). New formAction(target, options) binds a form to a server action that POSTs natively without JS and progressively enhances to a fetch-based submit with reactive pending / error / result state when JS is present (enhance(form) sets the native action/method and an optional hidden CSRF field, then intercepts submit). useFormStatus(action) exposes read-only status signals (mirroring React 19), and optimistic(base, reducer) is an optimistic-update primitive whose reactive value folds pending drafts over the base and reverts automatically (add / run / clear). Composes with the validation pipeline and the server module's csrf(). A non-OK response throws FormActionError (carrying status / response).
  • @bquery/bquery/formscreateFieldArray() gains an optional getKey for keyed list reconciliation (#139), plus keys() / keyAt(index). When supplied, the stable-key contract (present, unique keys) is validated on every structural mutation and a descriptive error names the offending key. Without getKey the array stays positional (unchanged behaviour).
  • @bquery/bquery/i18n — ICU MessageFormat support (#141). Messages using typed arguments ({count, plural, …}, {n, selectordinal, …}, {gender, select, …}) are routed through a locale-aware formatter backed by Intl.PluralRules, with offset:, exact =N selectors, nested arguments, the # token, and apostrophe escaping. New authoring helpers defineMessages() (identity + extraction anchor) and formatMessage() (standalone single-message formatter). Plain {name} interpolation and the legacy singular | plural pipe form are unchanged.
  • @bquery/bquery/i18n/extract — optional, dependency-free message-extraction tooling (#141). extractFromSource(), mergeCatalog(), extractFiles(), expandGlobs(), flatten() / unflatten(), and the CLI (runExtractCli, bquery-i18n extract) scan source for defineMessages catalogs and t() / tc() calls, then emit/merge nested JSON catalogs without overwriting existing translations (--prune opt-in). A separate entry point — importing it is never required at runtime, preserving the zero-build path.
  • @bquery/bquery/a11y — the runtime audit now stamps each AuditFinding with its WCAG 2.1 criterion (wcag), and the full rule catalog is exported as auditRules (#142) — each rule documents its WCAG mapping, default severity, and a known limitation (what it cannot detect).
  • @bquery/bquery/plugin — new definePlugin() authoring helper (#145): an identity helper that infers a plugin's install-options type and gives third-party authors a single, stable entry point.
  • @bquery/bquery/devtools — new stable, versioned bridge protocol for the DevTools browser extension (#146): connectDevtoolsBridge() (over window.postMessage), the transport-agnostic createBridgeServer(), serializeComponentTree(), and BRIDGE_PROTOCOL_VERSION / BRIDGE_SOURCE / BRIDGE_CAPABILITIES. A reference Manifest V3 extension (component tree, signal/store inspection, live timeline) ships in extension/.
  • @bquery/bquery/router + @bquery/bquery/server — opt-in, bundler-agnostic file-route convention with typed load / action (#149). New createFileRoutes(manifest, options?) turns a manifest (a bundler glob such as import.meta.glob, or a hand-written map) into the same RouteDefinitions createRouter() already consumes, with parseFilePath / filePathToRoutePattern (routes/users/[id]/+page.ts/users/:id, [...rest]*, (group) dropped) and specificity sorting (sortEntriesBySpecificity). Route modules export a typed Load (data into the view) and Action (mutation target). Loaders run on the server before render (the SSR router bridge now recognises meta.load alongside meta.loader) and on client navigation via createRouteData(router) / useRouteData(). The server module exposes mountFileRoutes(app, entries, options?) / createFileRouteServerRoutes() so a <form> (or formAction()) posts to a route's action, composing with csrf(). Programmatic routing stays fully supported and unchanged; no bundler is shipped. See the new File-based Routing guide.
  • Docs / Stability — single-source Stability Matrix plus a per-module stability changelog (#150). A new canonical STABILITY.md (backed by scripts/stability-matrix.mjs) records each module's maturity and its status-transition history; the README "Modules at a glance" table and the docs introduction.md matrix are now validated against it by bun run check:stability (scripts/check-stability-matrix.mjs), so the three surfaces can no longer silently drift.

Changed (1.15.0)

  • @bquery/bquery/viewview graduated to Stable in 1.15.0 (#136). The directive set and expression grammar are frozen for one minor cycle, and a per-directive SSR support matrix is published in the View guide.
  • @bquery/bquery/formsforms graduated to Stable in 1.15.0 (#139). The 1.13 batteries-included surface is frozen for one minor cycle; the 'manual' validationStrategy default is documented as a deliberate contract (handleSubmit() always runs the full validation pass; the strategy gates only automatic per-change/per-blur validation); the SSR serialization boundary is now a guaranteed contract (serializeFormState() deterministically drops functions, File / Blob / FileList, bigint, and symbol); and the createFieldArray() stable-key requirement is validated with clear errors. See the Forms guide.
  • @bquery/bquery/i18ni18n graduated to Stable in 1.15.0 (#141). The formatting/locale surface is frozen for one minor cycle, ICU MessageFormat coverage is documented and tested, and lazy-loading of catalogs is documented. See the i18n guide.
  • @bquery/bquery/a11ya11y graduated to Stable in 1.15.0 (#142). The surface (focus management, live regions, inert/scrollLock, preference signals) is frozen for one minor cycle, and the audit's WCAG coverage is documented with its known limitations. See the A11y guide.
  • @bquery/bquery/dnddnd graduated to Stable in 1.15.0 (#143). The surface is frozen for one minor cycle; the keyboard model (pick up / move / drop / cancel, aria-grabbed) is hardened and tested across grid / delay / viewport; and an accessibility statement is published. Drag announcements route through the shared a11y live-region announcer. See the DnD guide.
  • @bquery/bquery/mediamedia graduated to Stable in 1.15.0 (#144). The 1.14 composable surface is frozen for one minor cycle; each composable's SSR-safe default and cleanup is documented; and reactivity, idempotent destroy(), listener detachment, and AbortSignal teardown are verified. Bake-and-verify — no new features. See the Media guide.
  • @bquery/bquery/pluginplugin graduated to Stable in 1.15.0 (#145). T...
Read more

Version 1.14.2

Choose a tag to compare

@JosunLP JosunLP released this 26 Jun 09:42
d26b720

[1.14.2] - 2026-06-26

Fixed (1.14.2)

  • Updating Dev-Dependencies

Full Changelog: v1.14.1...v1.14.2

Version Release 1.14.1

Choose a tag to compare

@JosunLP JosunLP released this 28 May 17:54
15d301d

[1.14.1] - 2026-05-28

Fixed (1.14.1)

  • Motion: prefersReducedMotion() and reducedMotionSignal() now refresh their cached reduced-motion media query when window.matchMedia changes, preventing stale preference reads in tests and other environments that swap the media-query implementation at runtime.

What's Changed

  • Enhance concurrency, SSR, server runtime, and documentation updates by @JosunLP in #118

Full Changelog: v1.14.0...v1.14.1

Version 1.14.0

Choose a tag to compare

@JosunLP JosunLP released this 26 May 14:55
829dc2f

[1.14.0] - 2026-05-26

Added (1.14.0)

  • Media / Preference signals: Added usePreferredColorScheme(), usePreferredContrast(), usePreferredReducedTransparency(), usePreferredLanguage(), and usePreferredLanguages() reactive composables to @bquery/bquery/media that wrap prefers-color-scheme, prefers-contrast, prefers-reduced-transparency, and navigator.language(s) with deterministic SSR defaults.
  • Media / Page state: Added useOnlineStatus() (slim boolean variant of useNetworkStatus()), usePageVisibility(), useDocumentFocus(), useWindowFocus(), and useIdle(timeoutMs, opts?) to track top-level user-activity state.
  • Media / Element observers: Added useElementSize(target, opts?), useElementBounding(target, opts?), useElementVisibility(target, opts?), useHover(target), useFocus(target), useFocusWithin(target), and useActiveElement() — ergonomic wrappers over ResizeObserver / IntersectionObserver and DOM focus events. Targets accept plain Element | null | undefined values.
  • Media / Pointer & scroll: Added usePointer() ({ x, y, pressure, type, isInside }) and useScroll(target?) ({ x, y, directionX, directionY, isScrolling, arrived }).
  • Media / Platform integrations: Added usePermission(name) ('granted' | 'denied' | 'prompt' | 'unsupported'), useWakeLock() (isActive, request(), release()), useShare() / useShareSupported(), useBroadcastChannel<T>(name) ({ data, post, close }), useEventListener(target, event, opts?), useMediaDevices(), and useStorage<T>(key, defaultValue, opts?) with cross-tab storage event sync.
  • Media / Clipboard: Added clipboard.isSupported, clipboard.isImageSupported, clipboard.readImage(), clipboard.writeImage(), and the standalone clipboardText() reactive accessor.
  • Media / Composables: Every new composable accepts an optional { signal: AbortSignal } for auto-teardown matching the motion 1.13 convention, and an internal shared createMediaSignal helper standardises SSR safety + idempotent teardown.
  • Plugin / Hooks: Added a synchronous filter pipeline (addFilter, applyFilters, removeFilter, listFilters) and a fire-and-forget action bus (addAction, doAction, removeAction, listActions) exposed both on the install context (ctx.addFilter, ctx.addAction) and as standalone exports for app-level consumers.
  • Plugin / DI: Added container-level dependency injection — createInjectionKey<T>(), provide(key, value), inject(key), hasProvided(key), resetDi() — and a matching ctx.provide / ctx.inject. Plugins can register ctx.onCleanup(fn) callbacks that fire when the plugin is uninstalled.
  • Plugin / Lifecycle: Added unuse(name) and uninstall(name) to detach every directive, filter, action, and DI binding owned by a plugin and run its registered cleanups. install() may now return void | Promise<void>; concurrent installs of the same name are serialised.
  • Plugin / Metadata: BQueryPlugin now accepts optional version, description, and dependencies: string[]. use() enforces dependencies via dependencyMode: 'error' | 'warn'. New getPluginInfo(name) and getInstalledPlugins({ withMetadata: true }) overloads expose plugin metadata.
  • Plugin / Directives: Directives may now register lifecycle objects { mounted, unmounted } and use plugin-namespaced names like tooltip:arrow.
  • Devtools / Timeline: Timeline gained a ring buffer (maxTimelineEntries, default 1000) and TimelineEntry now carries optional payload, source, and duration. New event types: signal:create, signal:dispose, effect:dispose, component:mount, component:unmount, component:render, route:guard, error:caught, measure, mark.
  • Devtools / Querying: Added filterTimeline({ types, since, until, search }) and subscribeTimeline(listener) for live consumers.
  • Devtools / Inspection: Added privacy-aware inspectSignals({ includeValues: false }), structural diffSignals(prev, next) / diffStores(prev, next), traceSignal(label) / untraceSignal(label), and inspectEffects().
  • Devtools / Snapshots: Added exportDevtoolsSnapshot() and importDevtoolsSnapshot(json) for offline inspection and bug reports.
  • Devtools / Bridge: Added installBrowserBridge() that mirrors timeline events to window.__BQUERY_DEVTOOLS__.events for future browser-extension panels (no-op outside a DOM).
  • Devtools / Performance: Added time(label, fn), measureRender(tagName, fn), and getPerformanceSummary() aggregating event counts and average durations per type.
  • Testing / Cleanup: Added cleanup() to unmount any tracked render results from the current test, plus autoCleanup(beforeEach, afterEach) to wire it into bun:test.
  • Testing / Events: Attached shortcut methods to the existing fireEventfireEvent.click, fireEvent.dblClick, fireEvent.input(el, value), fireEvent.change(el, value), fireEvent.submit, fireEvent.focus, fireEvent.blur, fireEvent.keyDown, fireEvent.keyUp — and added a userEvent namespace (click, dblClick, hover, unhover, type(el, text, { delay? }), clear, selectOptions, tab, paste) that flushes effects + microtasks before returning.
  • Testing / Queries: Added a shadow-DOM-aware query layer — screen.getByRole/getByText/getByLabelText/getByPlaceholderText/getByTestId with query* and find* variants — and a within(root) factory that produces the same scoped query API.
  • Testing / Reactive helpers: Added mockComputed(fn) (with recomputeCount), mockEffect(fn) ({ runs, dispose }), tick() / nextTick(), flushPromises(), and runScheduled().
  • Testing / Mocks: Added mockStore<T>(initialState), mockI18n({ locale, messages }), mockForm<T>(initialValues), mockFetch(routes), and mockWebSocket() for isolated module testing.
  • Testing / Snapshots & a11y: Added prettyDOM(el, { maxLength, includeShadow }), getReactiveSummary(el), and expectAccessible(el) returning a structured AccessibilityResult for image-alt / button-name / label-input rules.
  • @bquery/bquery/router — additive 1.14.0 expansion:
    • NavigationResult type with pushResult() and replaceResult()
      methods that return structured results with status, requestedPath,
      to, from, and error fields instead of bare promises (existing
      push/replace continue to return Promise<void>).
    • beforeResolve(guard) global hook fired after beforeEach and
      route-level beforeEnter guards but before navigation commits.
    • resolveRoute(input) method for synchronous route lookup without
      navigating.
    • Dynamic route management via addRoute(parentName?, route),
      removeRoute(name), and hasRoute(name).
    • isReady() returning a promise that settles after the initial route
      synchronization during router construction,
      plus lastNavigation signal exposing the most recent result.
    • useNavigation() composable returning reactive navigation state
      (isNavigating, error, etc.).
  • @bquery/bquery/view — additive 1.14.0 expansion:
    • Public parseDirective(name) helper and ParsedDirective type for
      parsing bq-on:event.modifier-param.modifier syntax.
    • New directives bq-once, bq-init, bq-pre, bq-cloak,
      bq-html-safe, and bq-memo.
    • Full bq-on modifier system: .stop, .prevent, .self, .capture,
      .passive, .once, mouse-button filters (.left/.middle/.right),
      system-modifier filters (.ctrl/.alt/.shift/.meta), and
      KeyboardEvent.key filters including aliases (.enter, .esc, arrow
      keys, etc.).
  • @bquery/bquery/a11y — additive 1.14.0 expansion:
    • createLiveRegion(options) for imperative, per-instance ARIA live
      regions independent of the singleton announceToScreenReader.
    • Reactive keyboardUserSignal() and focusVisible() signals.
    • New media-preference signals prefersReducedTransparency(),
      prefersReducedData(), and forcedColors().
    • DOM helpers inert(target), scrollLock(), and autoFocus(target, opts).
  • @bquery/bquery/i18n — additive 1.14.0 expansion:
    • negotiateLocale(requested, available, opts) for pure locale
      negotiation against a list of available tags.
    • detectLocale(opts) reading from cookies, localStorage,
      <html lang>, and navigator.languages.
    • isRTL(locale) using Intl.Locale text-info when available with a
      well-known-language fallback.
    • New Intl helpers formatRelativeTime, formatList,
      formatDisplayName, and segment (graceful fallbacks when the
      underlying Intl API is unavailable).
  • @bquery/bquery/dnd — additive 1.14.0 expansion:
    • Programmatic API on existing handles — DraggableHandle.moveTo/reset/getPosition/setBounds/setAxis, SortableHandle.move/setOrder/getItems, DroppableHandle.setAccept/isOver/getActiveDragged.
    • New draggable options grid (snap-to-grid), delay (long-press threshold), touchStartThreshold (minimum pointer movement before drag activates), keyboard (opt-in keyboard accessibility with Space/Enter pickup, arrow-key movement, Escape cancel, and ARIA announcements via @bquery/bquery/a11y), and keyboardStep (keyboard movement step).
    • bounds now accepts an HTMLElement reference directly and supports a 'viewport' shorthand.
    • Reactive composables useDraggable(), useDroppable(), useSortable(), plus the draggablePosition() and sortableOrder() adapters for raw handles. Composables auto-dispose when the surrounding reactive scope stops.
  • @bquery/bquery/storybook: New ergonomic helpers classMap(), styleMap(), ifDefined(), repeat(), storyText(), and the opt-in sanitizer escape hatch unsafeHtml(), all callable inside storyHtml templ...
Read more

Version 1.13.0

Choose a tag to compare

@JosunLP JosunLP released this 21 May 14:18
ed3ab0d

[1.13.0] - 2026-05-21

Added (1.13.0)

  • Forms / Validators: Added a batteries-included set of tree-shakeable validators to @bquery/bquery/formsinteger, numeric, between, length, oneOf, notOneOf, arrayOf, requiredIf, requiredUnless, dateAfter, dateBefore, validDate, fileSize, fileType — plus combinators compose, all, not, and withMessage. (validDate is exported under that name to avoid collision with the existing isDate type guard in @bquery/bquery/core.)
  • Forms / Field state: Lifted isValidating, isFocused, and dirtySince signals onto every FormField. Added per-field helpers focus(), blur(), setValue(value, { touch, validate, silent }), setError(message), clearError(), a disabled signal that excludes the field from validation, and per-field validateOn / debounceMs parity with useFormField. FieldConfig now accepts parse and format for programmatic inbound/outbound value normalization.
  • Forms / Form state: Added submitCount, lastSubmittedAt, submitError, aggregated isValidating and isPristine, and helpers touchAll(), untouchAll(), resetField(name), resetErrors(), getDirtyValues(), and subscribe(listener). FormConfig now accepts onSubmitError, onSubmitSuccess, validationStrategy, and mode: 'all' | 'first'.
  • Forms / Field arrays: Added createFieldArray({ initial, factory, validators }) with add, remove, move, insert, clear, items, and length for dynamic repeating field groups.
  • Forms / Schema: Added a fluent schema({ name: field<string>().required().minLength(2), … }) helper that composes existing validator factories into a FieldConfig map.
  • Forms / DOM bindings: Added bindField(field, element, options?) and bindForm(form, formElement, options?) to bridge Form and FormField instances to standard inputs, selects, textareas, checkboxes, radios, file inputs, and [contenteditable] elements; both return cleanup functions. bindForm auto-discovers [name] inputs, marks aria-invalid, and supports a configurable error slot mapper.
  • Forms / Composables: Added scope-aware useForm, useField, and useFieldArray wrappers that auto-dispose with the owning component.
  • Forms / SSR: Added serializeFormState(id, form.snapshot()), readSerializedFormState(id), and hydrateForm(form, id) helpers (built on src/ssr/escape.ts) so server-rendered form state can resume on the client.
  • Component / Refs: Added useRef<T>() that auto-clears on disconnect.
  • Component / Slots: Added useSlot(host, name?) (reactive Signal<Element[]>), hasSlot(host, name?), and slotText(host, name?).
  • Component / Events: Added sanitizer-safe delegated event helpers on(event, handler), onClick, onInput, onChange, onSubmit, and bindDelegatedEvents(host). Handlers are stored in a module-level map keyed by opaque IDs; templates only carry data-bq-on-<event>="<id>" attributes.
  • Component / DI: Added provide(host, key, value), inject(host, key, fallback?), injectionKey<T>(description), and the formContextKey for letting inputs auto-bind to an enclosing <bq-form> without globals.
  • Component / Lifecycle: Added beforeUnmount and errorBoundary(error, info) hooks on ComponentDefinition, plus a scope-tracked whenIdle(fn) helper.
  • Component / Async: Added useAsync(fn) returning { data, error, loading, refresh } signals with AbortController-aware cancellation.
  • Component / Props: Added imperative setProp(name, value) and getProp(name) methods on every component instance for non-string objects (arrays, callbacks) that bypass attribute serialization.
  • Component / Styles: Added a css tagged template literal that produces a ComponentStyles payload. When Constructable Stylesheets are available the styles are shared via document.adoptedStyleSheets; otherwise the existing <style> element pathway is used. Interpolated values are CSS-escaped.
  • Component / Lists: Added keyedList(items, keyFn, renderItem) and reconcileKeyed(container) for keyed list rendering inside shadow DOM.
  • Motion / Easing: Full Penner easing family — easeIn/easeOut/easeInOut variants of Quart, Quint, Sine, Expo, Circ, Back, Elastic, and Bounce are now exported and mirrored in easingPresets. Added the cubicBezier(x1, y1, x2, y2) factory (Newton-Raphson refinement matching CSS cubic-bezier()), steps(count, position?) factory mirroring CSS steps(), and the mix(a, b, weight) / chain(...easings) composers.
  • Motion / Tweens: New animateValue<T>() and tween<T>() interpolate numbers, number arrays, or Record<string, number> between from and to using requestAnimationFrame. tween() returns full imperative controls (pause/resume/reverse/seek/stop/progress) with a finished promise, supports an AbortSignal, and respects prefers-reduced-motion.
  • Motion / animate() controls: animate() now accepts a signal: AbortSignal to cancel mid-flight and a playbackRate override. New animateTo(element, styles, opts) ergonomic wrapper turns a CSS property record (or [from, to] tuples) into keyframes.
  • Motion / Springs: spring() instances now expose .velocity(v?) and .set(v) for gesture-driven workflows. New springVector(dims, config) drives coordinated multi-dimensional motion. springPresets expands with wobbly, slow, and molasses presets.
  • Motion / Timeline: Timelines now support labels (addLabel(name, at?) + label-relative at strings like 'label+=200'), reverse(), playbackRate(n), repeat(count|'infinite'), yoyo(boolean), onUpdate(time) subscriptions, and a progress() getter in [0, 1].
  • Motion / New primitives: scrollProgress(element, opts) exposes a 0..1 scroll-linked stream; inView(element, opts) resolves a thenable on enter (with an optional reactive onChange callback); magnetic(element, opts), tilt(element, opts), shake(element, opts), pulse(element, opts), and countUp(element, from, to, opts) cover the micro-interaction toolkit. All effects honor prefers-reduced-motion by default.
  • Motion / Stagger: stagger() gains grid: [cols, rows] + from: { x, y } 2D origins, an axis: 'x' | 'y' distance restriction, and a deterministic random option (with optional randomSeed).
  • Motion / Reduced motion: onReducedMotionChange(callback) subscribes to changes (system preference or setReducedMotion() override) and returns an unsubscribe; reducedMotionSignal() exposes the same value as a reactive ReadonlySignal<boolean> for view/components.
  • Utils / Array (@bquery/bquery/core): Added groupBy, keyBy, partition, zip, range, first, last, take, drop, sample, shuffle (Fisher–Yates), uniqueBy, sortBy (single or multi-selector), intersection, difference, flattenDeep, move, and chunkBy.
  • Utils / Function: Added memoize(fn, keyFn?) (.clear() / .delete(key)), compose(...fns) / pipe(...fns), curry(fn), partial(fn, ...preset), and retry(fn, opts?) with exponential backoff, jitter, shouldRetry, onRetry, and AbortSignal support. debounce() gained an optional { leading?, trailing?, maxWait? } option bag plus a .flush() method; throttle() gained { leading?, trailing? } plus .flush(). Existing (fn, ms) signatures remain fully backward-compatible.
  • Utils / Object: Added prototype-pollution-safe deep accessors get(obj, path, default?), set(obj, path, value), and has(obj, path) with dot/bracket path syntax; mapValues, mapKeys, invert, deepEqual (with isEqual alias), freeze (deep), defaults(target, ...sources), and typed wrappers entriesTyped / keysTyped.
  • Utils / String: Added toSnakeCase, toPascalCase, toTitleCase, pad, padStart, padEnd, wordCount, safe template(str, vars) (${name} interpolation with no eval), DOM-free stripHtml, crypto-backed randomString(length, charset?), and universal-terminator lines(str).
  • Utils / Number: Added round(value, precision?), roundTo(value, step), lerp, inverseLerp, mapRange, locale-aware formatBytes(bytes, opts?) (decimal & binary units), randomFloat, sum, average, median, degToRad, and radToDeg.
  • Utils / Misc: Added RFC 4122 v4 uuid() (uses crypto.randomUUID() / getRandomValues() when available, with a Math.random() fallback), Go-style sync/async tryCatch(fn), times(n, fn), pollUntil(predicate, opts?), nextFrame(), and nextTick().
  • Utils / Type guards: Added isError, isMap, isSet, isRegExp, isSymbol, isBigInt, isAsyncFunction, isIterable, isAsyncIterable, isNullish, and isDefined.
  • The utils namespace and BQueryUtils interface include every new entry alongside the existing helpers.

Changed (1.13.0)

  • Full bundle: src/full.ts re-exports every new public forms, component, and motion runtime/type surface alongside the new core utility helpers; bun run check:full-bundle continues to enforce drift detection.
  • AI guidance: AGENT.md, llms.txt, copilot-instructions, Cursor / Cline rules, README, and CHANGELOG were refreshed for the 1.13.0 baseline. bun run check:ai-guidance passes.

What's Changed

  • Switch documentation domain metadata to bquery.js.org by @Copilot in #98
  • Version 1.13.0 by @JosunLP in #97

Full Changelog: v1.12.0...v1.13.0

Version Release 1.12.0

Choose a tag to compare

@JosunLP JosunLP released this 16 May 21:19
bb2959d

[1.12.0] - 2026-05-16

Added (1.12.0)

  • Reactive / WebSocket: Promoted WebSocketSendData to a public type-only export from @bquery/bquery/reactive. The alias was previously @internal even though it already surfaced through UseWebSocketReturn.sendRaw, WebSocketSerializer.serialize, and WebSocketHeartbeatConfig.message. Consumers can now import type { WebSocketSendData } from '@bquery/bquery/reactive' to reuse the union, matching the existing ServerWebSocketData export from @bquery/bquery/server.
  • Store / Plugins: Added unregisterPlugin(plugin) and clearPlugins() to @bquery/bquery/store. unregisterPlugin() removes the first matching registration by identity and returns whether one was found; clearPlugins() empties the registry in one call. Already-created stores keep extensions that were applied before unregister; subsequent defineStore() / createStore() calls no longer receive the removed plugins. The previously global, append-only plugin registry now has a proper teardown path for test isolation and runtime plugin reloads.

Changed (1.12.0)

  • Docs / Server: Expanded the server guide with a public-surface reference, commonly used server types, null-prototype params / query details, route-scoped middleware examples, custom error handling, and WebSocket middleware short-circuit behavior. Added server module export tests for the barrel, root entry point, and full bundle.

Fixed (1.12.0)

  • Full bundle / Tooling: src/full.ts now re-exports all public type-only module exports from the platform, a11y, and media barrels, and bun run check:full-bundle now validates runtime and type exports statically so /full declaration drift is caught before release.

What's Changed

  • 1.12..0 Sync full bundle and enhance WebSocket support with new types by @JosunLP in #93

Full Changelog: v1.11.1...v1.12.0

Version 1.11.1

Choose a tag to compare

@JosunLP JosunLP released this 12 May 10:15
07f5cda

[1.11.1] - 2026-05-12

Changed (1.11.1)

  • Tooling / Dev dependencies: Bumped @typescript-eslint/eslint-plugin and @typescript-eslint/parser from 8.59.1 to 8.59.3, eslint from 10.2.1 to 10.3.0, globals from 17.5.0 to 17.6.0, and vite from 8.0.10 to 8.0.12. The vite update brings in the stable [email protected] release (previously 1.0.0-rc.17) and [email protected].

What's Changed

  • docs: repo-wide messaging update — README header redesign, centred layout, updated branding by @Copilot in #90
  • Redesign README layout and update framework messaging by @JosunLP in #91

Full Changelog: v1.11.0...v1.11.1