Skip to content

Release 1.16.0 — quality & performance pass - #203

Merged
JosunLP merged 5 commits into
mainfrom
dev
Aug 11, 2026
Merged

Release 1.16.0 — quality & performance pass#203
JosunLP merged 5 commits into
mainfrom
dev

Conversation

@JosunLP

@JosunLP JosunLP commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Release 1.16.0 — Quality & performance pass

Promotes dev to main for the 1.16.0 minor release. A full audit of the framework's three hot paths — the reactive core, the DOM core, and the view layer — drove measurable speed-ups and surfaced several real correctness bugs, all fixed here. The release also folds in the previously staged follow-up to the 1.15.1 security review, plus a documentation-wide accuracy pass.

No breaking changes. No module status transitions. The only new API is the additive trailing option on watchThrottle.

Performance (benchmarked old vs. new)

Area Change Measured effect
reactive batch() keeps the batch open while flushing; transitive updates coalesce, re-queued observers drain in follow-up passes (bounded at 100) diamond dependencies: 1 effect run per batch instead of 1 per branch
reactive Computed re-validates and notifies subscribers only when the value actually changed (Object.is) unchanged computed bool under 20k writes: 20k effect runs → 2
reactive allocation cuts on signal writes and dependency tracking writes w/o subscribers ~6× faster; repeated reads ~1.6×; computed chains ~1.7×
core replaceWith(string) sanitizes once per call; empty()replaceChildren(); children()/siblings()/index()/unwrap()/css(object)/data() shed per-element allocations fewer sanitizer runs, HTML parses, and array copies on collection ops
view per-update work moved to bind time: object-expression parsing, transition resolution, memoized directive parsing, sandbox proxies cached per context; unchanged DOM writes skipped less garbage and fewer DOM writes per reactive tick, faster bq-for reconciles

Correctness fixes

Area Fix
core undelegate() from a fresh wrapper (the documented usage) was a silent no-op leaking the listener — registry is now module-level, delegate() idempotent per (element, event, selector, handler); delegated dispatcher tolerates non-Element targets
core wrap(element) over multi-element collections no longer clones previously wrapped elements into later wrappers
view directives declared before bq-for on the same element no longer bind to the discarded template element
view bq-once/bq-memo/bq-init evaluate untracked — they no longer subscribe the enclosing bq-for reconciler
view bq-model no longer resets the caret while typing (skips the write-back when the input already holds the value)
view children of bq-html/bq-html-safe are no longer directive-bound (effects kept running against replaced markup)
reactive nested batch() no longer double-executes observers; a throwing computed stays dirty and retries instead of serving a stale cache
motion onReducedMotionChange re-binds to the current window.matchMedia on subscribe and flushes changes that happened without a change event (also fixes two order-dependent motion tests)
store deepClone special-cases only the dangerous __proto__ key; properties merely named constructor/prototype clone normally again
view/compiler rejects legacy leading-zero decimal literals instead of emitting strict-mode SyntaxErrors

Security (1.15.1 follow-up)

  • with-scope evaluator hardening closes a residual member-access escape (items.constructor.constructor('…')()) via the shared hasDangerousMemberAccess() guard, applied to both the runtime evaluator and the AOT compiler (#168, #202).

Additive API (backwards-compatible)

  • watchThrottle(source, cb, interval, { trailing: true }) — delivers the last value of a burst once the interval elapses; default stays leading-edge-only (WatchThrottleOptions, @bquery/bquery/reactive).

Behavioral notes (no opt-out needed)

  • Effects depending on a computed re-run without a value change now run less often — matching the documented contract. Read the underlying signal directly if you relied on the extra runs.
  • delegate() no longer stacks duplicate listeners for the same handler tuple.

Documentation accuracy pass

  • ~40 factual errors fixed across the view/reactive/core guides and concepts pages (nonexistent APIs like .set()/cloneDeep/bq-attr removed, bq-memo/bq-error/bq-model described as implemented, broken examples repaired).
  • 20 broken in-site anchors (cookbook/workflows → reactive guide) and the contradicting 1.15.0 release date (now consistently 2026-06-30) fixed.
  • AI guidance files carry full 1.15.1 + 1.16.0 sections; check:doc-exports now reports 100 % export coverage for all 21 modules.

Release chores

  • package.json: 1.15.1 → 1.16.0 (the staged 1.15.2 patch was folded into this minor)
  • CHANGELOG.md: new [1.16.0] section (Added/Changed/Fixed/Security) and TOC entry
  • Docs: new release-notes/1.16 page, nav + index entries, version-history entries in the affected guides
  • Dev dependencies: Storybook 10.5.7, typescript-eslint 8.67.0, ESLint 10.8.1, happy-dom 20.11.2, Prettier 3.9.6, Vite 8.2.1

Validation

  • bun test: 3056 pass / 0 fail (8 new regression tests)
  • tsc --noEmit (src + component-test config): clean
  • bun run build (ESM + UMD + types) and bun run docs:build: succeed
  • check:stability, check:full-bundle, check:ai-guidance, check:doc-exports (21/21 modules at 100 %): pass

🤖 Generated with Claude Code

…est files

- Refactored test cases in `ssr-runtime.test.ts` for better readability by aligning parameters and breaking long lines.
- Updated `ssr-stable.test.ts` to enhance clarity by formatting multi-line function calls.
- Cleaned up `storybook.test.ts` by consolidating assertions into single lines.
- Simplified `testing-extensions.test.ts` by streamlining error expectations.
- Enhanced `utils-array-extras.test.ts` and `utils-function-extras.test.ts` for better visual structure.
- Improved `utils-misc-extras.test.ts` and `utils-object-extras.test.ts` by formatting complex assertions.
- Refined `view-1-14.test.ts` and `view-compiler.test.ts` for consistency in line breaks and indentation.
- Added new tests in `view-process-order.test.ts` to cover directive processing order and subscription hygiene.
- Updated `view-stable.test.ts` and `view-transitions.test.ts` for better readability and consistency in test structure.
- Enhanced `view.test.ts` for clarity in handler invocation tests.
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 187 files, which is 87 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f9d3e1b9-2561-409e-be45-385298c07fab

📥 Commits

Reviewing files that changed from the base of the PR and between 3f733c6 and c612ef4.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (187)
  • .clinerules
  • .cursorrules
  • .github/ISSUE_TEMPLATE/bug-report.yml
  • .github/ISSUE_TEMPLATE/feature-request.yml
  • .github/copilot-instructions.md
  • .github/dependabot.yml
  • AGENT.md
  • CHANGELOG.md
  • README.md
  • docs/.vitepress/config.ts
  • docs/concepts/architecture.md
  • docs/concepts/bundle-and-tree-shaking.md
  • docs/concepts/reactivity-model.md
  • docs/concepts/runtimes.md
  • docs/concepts/security-model.md
  • docs/contributing/agent-guide.md
  • docs/contributing/release-process.md
  • docs/contributing/repo-layout.md
  • docs/cookbook/debounced-search.md
  • docs/cookbook/infinite-scroll.md
  • docs/cookbook/login-form.md
  • docs/cookbook/mock-fetch-test.md
  • docs/cookbook/modal-dialog.md
  • docs/cookbook/notification-toast.md
  • docs/cookbook/pagination.md
  • docs/cookbook/persisted-store.md
  • docs/cookbook/polling.md
  • docs/cookbook/scroll-reveal.md
  • docs/cookbook/sse-stream.md
  • docs/cookbook/ssr-deploy.md
  • docs/cookbook/trace-signal.md
  • docs/cookbook/websocket-reconnect.md
  • docs/glossary.md
  • docs/guide/api-core.md
  • docs/guide/best-practices.md
  • docs/guide/components.md
  • docs/guide/concurrency.md
  • docs/guide/devtools.md
  • docs/guide/file-routing.md
  • docs/guide/forms.md
  • docs/guide/motion.md
  • docs/guide/reactive.md
  • docs/guide/router.md
  • docs/guide/server.md
  • docs/guide/ssr.md
  • docs/guide/store.md
  • docs/guide/tutorial.md
  • docs/guide/view.md
  • docs/index.md
  • docs/release-notes/1.14.md
  • docs/release-notes/1.15.md
  • docs/release-notes/1.16.md
  • docs/release-notes/index.md
  • docs/workflows/backend-api.md
  • docs/workflows/component-library.md
  • docs/workflows/forms-validation.md
  • docs/workflows/index.md
  • docs/workflows/off-main-thread.md
  • docs/workflows/sortable-keyboard.md
  • docs/workflows/streaming-ssr.md
  • docs/workflows/todo-spa.md
  • eslint.config.js
  • extension/panel.html
  • llms.txt
  • package.json
  • scripts/check-doc-exports.mjs
  • scripts/check-full-bundle.mjs
  • scripts/check-stability-matrix.mjs
  • src/a11y/keyboard-signals.ts
  • src/a11y/live-region.ts
  • src/a11y/skip-link.ts
  • src/component/async.ts
  • src/component/css.ts
  • src/component/events.ts
  • src/component/index.ts
  • src/component/inject.ts
  • src/component/keyed-list.ts
  • src/concurrency/high-level.ts
  • src/concurrency/internal.ts
  • src/concurrency/module-worker.ts
  • src/concurrency/types.ts
  • src/core/collection.ts
  • src/core/element.ts
  • src/core/shared.ts
  • src/core/utils/array.ts
  • src/core/utils/function.ts
  • src/core/utils/misc.ts
  • src/core/utils/object.ts
  • src/core/utils/string.ts
  • src/core/utils/type-guards.ts
  • src/devtools/bridge.ts
  • src/dnd/draggable.ts
  • src/dnd/droppable.ts
  • src/dnd/reactive.ts
  • src/dnd/sortable.ts
  • src/forms/action.ts
  • src/forms/bind.ts
  • src/forms/create-form.ts
  • src/forms/index.ts
  • src/forms/schema.ts
  • src/forms/ssr.ts
  • src/forms/validators.ts
  • src/full.ts
  • src/i18n/formatting-extras.ts
  • src/media/browser-state.ts
  • src/media/clipboard.ts
  • src/media/dom-targets.ts
  • src/media/observers.ts
  • src/motion/animate.ts
  • src/motion/easing.ts
  • src/motion/index.ts
  • src/motion/reduced-motion.ts
  • src/motion/scroll-progress.ts
  • src/motion/stagger.ts
  • src/motion/timeline.ts
  • src/motion/tween.ts
  • src/plugin/index.ts
  • src/reactive/computed.ts
  • src/reactive/core.ts
  • src/reactive/effect.ts
  • src/reactive/index.ts
  • src/reactive/internals.ts
  • src/reactive/signal.ts
  • src/reactive/watch.ts
  • src/router/file-routes/routes.ts
  • src/router/index.ts
  • src/router/match.ts
  • src/router/router.ts
  • src/router/use-navigation.ts
  • src/router/utils.ts
  • src/server/file-routes.ts
  • src/ssr/hydration.ts
  • src/ssr/render-async.ts
  • src/ssr/renderer.ts
  • src/ssr/resumable-boundary.ts
  • src/storybook/helpers.ts
  • src/storybook/story-html.ts
  • src/storybook/story-svg.ts
  • src/testing/testing.ts
  • src/view/directives/aria.ts
  • src/view/directives/bind.ts
  • src/view/directives/class.ts
  • src/view/directives/for.ts
  • src/view/directives/html.ts
  • src/view/directives/if.ts
  • src/view/directives/lightweight.ts
  • src/view/directives/model.ts
  • src/view/directives/on-modifiers.ts
  • src/view/directives/show.ts
  • src/view/directives/style.ts
  • src/view/directives/text.ts
  • src/view/directives/transitions.ts
  • src/view/evaluate.ts
  • src/view/mount.ts
  • src/view/process.ts
  • tests/a11y-1-14.test.ts
  • tests/component-extensions.test.ts
  • tests/component.test.ts
  • tests/computed-revalidation.test.ts
  • tests/concurrency-stable.test.ts
  • tests/core.test.ts
  • tests/devtools-extensions.test.ts
  • tests/dnd.test.ts
  • tests/forms-actions.test.ts
  • tests/forms-extensions.test.ts
  • tests/motion-effects.test.ts
  • tests/motion-timeline-extras.test.ts
  • tests/motion.test.ts
  • tests/plugin-extensions.test.ts
  • tests/repro-readd.test.ts
  • tests/server-file-routes.test.ts
  • tests/server.test.ts
  • tests/signal.test.ts
  • tests/ssr-runtime.test.ts
  • tests/ssr-stable.test.ts
  • tests/storybook.test.ts
  • tests/testing-extensions.test.ts
  • tests/utils-array-extras.test.ts
  • tests/utils-function-extras.test.ts
  • tests/utils-misc-extras.test.ts
  • tests/utils-object-extras.test.ts
  • tests/view-1-14.test.ts
  • tests/view-compiler.test.ts
  • tests/view-process-order.test.ts
  • tests/view-stable.test.ts
  • tests/view-transitions.test.ts
  • tests/view.test.ts

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added core Changes to the core module reactive Changes to the reactive module component Changes to the component module motion Changes to the motion module router Changes to the router module view Changes to the view module docs Changes to the documentation tests Chenges to the tests build Changes to the build and meta files github Changes to the github meta data files forms Changes to the forms module i18n Changes to the i18n module a11y Changes to the a11y module dnd Changes to the dnd module media Changes to the media module plugin Changes to the plugin module devtools Changes to the devtools module testing Changes to the testing module ssr Changes to the ssr module storybook Changes to the storybook module concurrency server labels Aug 11, 2026
@JosunLP JosunLP self-assigned this Aug 11, 2026
JosunLP and others added 2 commits August 11, 2026 13:24
Signed-off-by: Jonas Pfalzgraf <[email protected]>
…bscribe

onReducedMotionChange flushed pending value changes before ensuring the
matchMedia subscription existed. When a baseline (lastDispatchedValue) was
left behind by setReducedMotion() without an active subscription — a state
CI's test-file ordering produced — the flush issued a one-off matchMedia
call and ensure() then subscribed again, so a freshly mocked matchMedia saw
two calls instead of one. Establish the subscription first so evaluation
reads the cached media query; add a deterministic regression test for the
baseline-without-subscription state.

Co-Authored-By: Claude Fable 5 <[email protected]>
@JosunLP JosunLP added this to the 1.16.0 milestone Aug 11, 2026

@JosunLP JosunLP left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review (workflow-assisted, high effort): 8 findings — 5 confirmed against the code, 3 plausible. Ranked by severity in the inline comments. The two computed.ts issues can permanently freeze subscribers and look like release blockers for 1.16.0.

Comment thread src/reactive/computed.ts Outdated
Comment thread src/reactive/computed.ts Outdated
Comment thread src/view/process.ts Outdated
Comment thread src/reactive/internals.ts
Comment thread src/view/directives/text.ts Outdated
Comment thread src/core/shared.ts Outdated
Comment thread src/view/directives/if.ts Outdated
Comment thread src/view/process.ts
Correctness:
- computed: markDirty no longer early-returns on an already-dirty computed.
  A compute that threw (or a revalidate dropped by a flush-pass overflow)
  left dirty=true with no pending revalidate, so every later dependency
  change was ignored and subscribers stayed frozen.
- computed: rebase the notified-value baseline when a subscriber joins a
  previously empty set, so a change made while nobody was subscribed
  followed by a revert is no longer swallowed by the equality check. Adds a
  stale-baseline guard for readers that observe an un-notified value.
- view/process: restore the children snapshot in processChildren. The live
  nextElementSibling walk silently dropped every remaining sibling once a
  directive handler removed the node it was standing on.
- view/text: only skip the write when the element already is exactly that
  single text node. Comparing el.textContent alone left server-rendered
  markup (<b>Hel</b>lo) in place instead of flattening it.
- view/if,show: resolve transitions inside the effect again so attributes
  added or changed after mount take effect (bq-if resolves per flip).
- core/shared: count delegated registrations so one owner's undelegate()
  cannot detach a delegation another owner still holds.

Efficiency:
- view/process: bound the parseDirective memo like the expression caches so
  generated attribute names cannot grow it without eviction.

Co-Authored-By: Claude Opus 5 <[email protected]>
tsconfig.component-test.json targets a lib without es2022 array methods,
so `.at(-1)` failed the test type-check in CI.

Co-Authored-By: Claude Opus 5 <[email protected]>
@JosunLP
JosunLP merged commit 6eb76be into main Aug 11, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a11y Changes to the a11y module build Changes to the build and meta files component Changes to the component module concurrency core Changes to the core module devtools Changes to the devtools module dnd Changes to the dnd module docs Changes to the documentation forms Changes to the forms module github Changes to the github meta data files i18n Changes to the i18n module media Changes to the media module motion Changes to the motion module plugin Changes to the plugin module reactive Changes to the reactive module router Changes to the router module server ssr Changes to the ssr module storybook Changes to the storybook module testing Changes to the testing module tests Chenges to the tests view Changes to the view module

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant