Repository navigation
Fix SSR header forwarding and view imports with a query - #1
Merged
Merged
Conversation
A render entry that returns a fetch()ed Response hands the middleware an already decoded body, but pipeResponse() copied every header verbatim, including content-encoding and the compressed content-length. Browsers then failed to decode the body or truncated it. Only the HTML branch filtered these headers. Both branches now share copyHeaders(), which drops content-encoding, content-length, transfer-encoding and the hop-by-hop headers (plus any named in Connection), and keeps every Set-Cookie value via getSetCookie(). The dev-server tests proxy a gzip upstream through the middleware for a JSON and an HTML response. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Ju3jbgag99iuxndoJTrgM
resolveId() rewrote every *.view.html import to `<abs>?bq-view` and dropped the importer's query, so in Vite `import url from './home.view.html?url'` (or ?raw, ?inline) returned the compiled view module instead of a URL or the markup. The plugin now resolves and loads only bare template imports and ids whose query holds just its own marker, ignoring the `import` and `t=` parts the Vite dev server adds for transport and HMR. Anything else is left to the bundler. Tests cover ?raw and ?url in a real Vite build and in the dev server, the bare import, and recompiling an edited view on an HMR re-import. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Ju3jbgag99iuxndoJTrgM
Newer bQuery releases register compiled bq-on handlers in a separate
statements map (compileViews() returns { expressions, statements, stats }
and emitModule() forwards it). The plugin passes the whole compile result
to emitModule(), so only the test's assertion on the emitted line format
needed loosening. It still holds for the published 1.17.2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Ju3jbgag99iuxndoJTrgM
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configuration
📒 Files selected for processing: 6
✨ Finishing Touches
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. Comment |
10 tasks done
JosunLP
added a commit
to bQuery/bQuery
that referenced
this pull request
Oct 21, 2026
…pendency updates (#278) ## Summary This PR covers everything that was still open after #277: - **Security:** fixes for sanitizer gaps found by the adversarial corpus from #230 and by code review. - **`create-bquery`** (#225): scaffolder under `packages/create-bquery`. It is published standalone for `npm create bquery`, and `@bquery/bquery` also ships it as its `create-bquery` bin. - **Tooling packages moved out:** `@bquery/eslint-plugin` (#228) and `@bquery/unplugin` (#224) were built here, then moved to [bQuery/eslint-plugin](https://github.com/bQuery/eslint-plugin) and [bQuery/unplugin](https://github.com/bQuery/unplugin), because they are not shipped with `@bquery/bquery`. - **Library fixes:** bugs found while writing the scaffolder templates, plus the follow-ups from a code review. - **Dev dependency updates:** this includes the majors happy-dom 21 and unplugin 3. ## Motivation Closes #224, closes #225, closes #228. The plugin code lives in its own repositories now. Each has a draft PR with the review fixes: bQuery/eslint-plugin#1 and bQuery/unplugin#1. ## Changes included **Security** (`1d8f00d`, `35a4bd3`) - **Comments in SSR sanitizing:** `sanitizeHtmlForSSR()` kept HTML comments verbatim. Browsers end a comment early at `<!-->`, `<!--->` and `--!>`, which allowed XSS through `bq-html` in SSR. Comments are now dropped. - **URL attributes in SSR sanitizing:** `sanitizeHtmlForSSR()` now checks the full URL attribute list and every `srcset` candidate, not just `href`/`src`. - **Text-only elements in the DOM backend:** - `sanitizeHtml()` drops every non-text child of elements a browser parses as text (`textarea`, `title`, `xmp`, …), comments included. - `<textarea><!--</textarea><img onerror=…>--></textarea>` still broke out through a comment under happy-dom before `35a4bd3`. - The DOM backend now also drops comments everywhere, like the other two paths. - **Tests:** the corpus has 129 payloads. The property-based pass covers all three sanitizer paths. **`create-bquery`** (#225: `3026078`, `ed47d6d`, `c454576`) - `npm create bquery@latest` with four templates: CDN, SPA, SSR (Node/Bun/Deno) and full-stack. Each ships tests wired to `@bquery/bquery/testing`. - `test/templates.test.ts` scaffolds every variant against the sources and runs its tests, type-check, SPA build and a server smoke run. - `@bquery/bquery` ships the scaffolder as its `create-bquery` bin, with `packages/create-bquery/{src,templates,package.json}` in `files`. `npx create-bquery` works wherever bQuery is installed and always matches the installed release. - The standalone package's `prepublishOnly` refuses to publish until the bQuery release its templates target is on npm. **Tooling packages moved out** (`c454576`; built in `7adf83e`, `9f4d301`, `78d0866`, `f673f0b`) - `packages/eslint-plugin` and `packages/unplugin` are removed. Their history up to `980d0cc` was imported into the new repositories, where they run standalone against the published `@bquery/bquery`. - The guides point to the new repositories, and the root `unplugin` devDependency is dropped. **Library fixes** - **SSR** (`57b846a`, `3b42593`): - Full pages keep their doctype (rebuilt, not copied) and their `<html>/<head>/<body>`, with or without a global DOM. - `renderToStream()` streams a page with `flushBoundary()` markers as one document; previously the first chunk closed `</body></html>`. - `renderToStreamSuspense()` holds the closing tags until its patches are out. - Document detection skips a BOM, whitespace and comments and accepts legacy doctypes, using a linear scanner. - **Reactive** (`184cce6`, `9f005ce`): `HttpError.response.data` carries the parsed error body. The read is bounded: 1 MiB, within the remaining `timeout` (5 s when none is set), and abortable. A never-ending error body can no longer hang the request. `onDownloadProgress` reports successful bodies only. - **View** (`7c31c4a`, `ad85e44`): - `bq-on` accepts `;`-separated statements. - The compiler keys compiled code by mode: `registerCompiledExpressions(expressions, statements?)`. A source shared between `bq-on` and another directive now compiles the same way it runs, and the handler stays CSP-safe. - `view/compiler` no longer bundles the runtime evaluator. - **Store** (`e6ce336`): `createStore` / `defineStore` / `createPersistedStore` infer getters and actions. - **Testing** (`f2bf0de`): - `*ByRole({ name })`. - Real `MouseEvent` / `KeyboardEvent` / … objects from `fireEvent` / `userEvent`. - `getByLabelText` finds nested label text. - **Budgets** (`980d0cc`): `./ssr` 39.8 kB, `./server` 42.3 kB, `./testing` 9.6 kB, each with a note. The docs size table is refreshed. **Docs, CHANGELOG, CI** (`22cee8d`, `9137586`): "Tooling" sidebar group, cross-links, CHANGELOG entries, and `test:types` type-checks `packages/`. **Dependencies** (`01dd1e0`) - In range: storybook 10.6.3, @typescript-eslint 8.72.0, eslint 10.13.0, prettier 3.9.10, vite 8.3.5, bun-types 1.4.3, @playwright/test 1.64.1. - Majors: happy-dom 21.0.4, unplugin 3.4.0. The unplugin devDependency is now dropped again, since the plugin left the repo. - **TypeScript stays at 6.0.3.** TypeScript 7 drops the JS compiler API that typescript-eslint, typedoc and two of our generators depend on. ## Validation - [x] `bun run lint`, `bunx tsc --noEmit`, `bun run test:types`, `bun run format:check` - [x] `CI=true bun test`: 4664 pass, 0 fail - [x] `bun run build`, `check:size` (all 25 entries within budget), `check:package` (1.49 MB packed) - [x] `bun run check` - [x] `npx create-bquery` from the repo root scaffolds a project; `packages/create-bquery/test/bin.test.ts` covers the bin - [x] `bun run build:docs` passed in CI on the previous head ## Checklist - [x] I followed the contributing guidelines. - [x] I added or updated tests where behavior changed. - [x] I updated documentation when public behavior or developer workflow changed. - [x] I confirmed the change does not introduce security-sensitive regressions. ## Notes for reviewers - The security fixes become public with this PR. That was a deliberate decision by the maintainer. - **Publishing:** `create-bquery` (standalone), `@bquery/eslint-plugin` and `@bquery/unplugin` each need an npm Trusted Publisher. The two plugin repositories have a manual `publish.yml` for that. - **Scaffolder versions:** `BQUERY_VERSION` must equal the root version (a test enforces it), so the bundled scaffolder always targets the release it ships in. - **Frozen grammar:** `bq-on` statement lists widen the expression grammar of a frozen directive. No directive is added. - **Testing behaviour change:** clicks are real `MouseEvent`s now, so happy-dom runs default actions (submit, checkbox toggle), as a browser does. - **History:** the source deletion of the two plugin packages landed in `35a4bd3` together with the sanitizer fix; the rest of the move is `c454576`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_018Ju3jbgag99iuxndoJTrgM --------- Co-authored-by: Claude <noreply@anthropic.com>
JosunLP
marked this pull request as ready for review
October 21, 2026 23:14
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes two review findings from bQuery/bQuery#278. The plugin was developed there and moved into this repository;
mainholds that import, now working against the published@bquery/bquery@1.17.2.54bc81f):Responsewhose body the middleware reads decoded, such as afetch()ed upstream, kept its originalcontent-encoding: gzipand compressedcontent-length. Browsers then failed to decode the body or truncated it.copyHeaders()helper now serves both branches. It drops encoding, length,transfer-encoding, hop-by-hop headers and any header named inConnection, and keeps everySet-Cookie.52764a2):resolveIdrewrote every*.view.htmlimport to the compiled view, so?raw,?urland?inlineimports got template code instead.import/t=query parts are still recognised, so HMR keeps working.1e4c158): an assertion on the emitted module now also accepts the separatestatementsmap that the next bQuery release passes toregisterCompiledExpressions().Validation
bun run typecheck,bun run lint,bun run format:checkCI=true bun test: 55 pass, including:?raw/?urlin a real Vite buildThree of the new tests fail without the fix.
Notes
@bquery/bquery@1.17.2,bq-onstatement lists ($event.preventDefault(); add()) cannot be compiled ahead of time. The plugin warns and leaves them to the runtime evaluator, so the build doesn't break. The README covers this.statementsmap. The plugin already forwards it, because it passes the wholecompileViews()result toemitModule().bQuery/unpluginandpublish.yml(a manual workflow, dry-run by default).Generated by Claude Code