feat(search): add useSearchCollection composable with FTS5 full-text search#3787
Conversation
…t search Co-Authored-By: Sébastien Chopin <atinux@gmail.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
commit: |
|
All alerts resolved. Learn more about Socket for GitHub. This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored. |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThis PR introduces SQLite FTS5-backed full-text search to Nuxt Content via a new Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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 |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (3)
docs/package.json (1)
17-17: ⚡ Quick winMove
drizzle-kittodevDependencies.
drizzle-kitis a CLI tool for schema migrations and generation, not a runtime dependency. It should be indevDependenciesto avoid including it in production installs. No runtime usage of this package was found in the codebase.Suggested change
"dependencies": { "@libsql/client": "^0.17.2", "@nuxt/content": "link:..", "@nuxthub/core": "^0.10.7", "@nuxtjs/plausible": "^3.0.2", "@vercel/analytics": "^2.0.1", "@vercel/speed-insights": "^2.0.0", "@vueuse/nuxt": "^14.2.1", "better-sqlite3": "^12.8.0", "docus": "^5.8.1", - "drizzle-kit": "^0.31.10", "drizzle-orm": "^0.45.1", "minisearch": "^7.2.0", "nuxt": "^4.4.2", "nuxt-studio": "^1.5.1" + }, + "devDependencies": { + "drizzle-kit": "^0.31.10" }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/package.json` at line 17, The package.json currently lists "drizzle-kit" in dependencies; move "drizzle-kit" into devDependencies instead because it's a CLI/build tool not required at runtime: remove the "drizzle-kit" entry from the top-level "dependencies" and add the same version string under "devDependencies" (preserve the version "^0.31.10"), then update your lockfile by running your package manager (npm/yarn/pnpm install) to reflect the change; verify no runtime imports reference "drizzle-kit" and commit the modified package.json and updated lockfile.src/runtime/client.ts (2)
100-100: ⚖️ Poor tradeoffConsider adding a cleanup method.
The composable doesn't expose a way to dispose of the
DatabaseAdapter. If the component using this composable is unmounted or the collections change significantly, the database adapter and FTS index remain in memory.Consider adding a
destroy()orcleanup()method to the return object that closes the database adapter if the underlying implementation supports it:function destroy() { db = undefined initPromise = undefined indexedFor = [] status.value = 'idle' } return { status, search, init, destroy }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/runtime/client.ts` at line 100, Add a cleanup method to the composable that disposes the DatabaseAdapter and resets internal state: implement a destroy or cleanup function that, if the underlying DatabaseAdapter supports a close/closeAsync method, calls it, then sets db = undefined, initPromise = undefined, indexedFor = [], and status.value = 'idle'; finally include this method in the returned object alongside status, search, and init so callers can explicitly release resources.
74-74: 💤 Low valueType assertion bypasses collection key validation.
The cast
col as Tassumes that the string fromtoIndexis a valid collection key, but the type system doesn't verify this at runtime. While the function signature constrains the input tokeyof PageCollections, the string manipulation path throughresolveCollections()erases that type information.This is acceptable given the constraints, but if stricter type safety is desired, consider maintaining typed arrays throughout:
function resolveCollections(): T[] { const val = toValue(collection) return (Array.isArray(val) ? val : [val]) as T[] }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/runtime/client.ts` at line 74, The cast `col as T` in the line calling queryCollection(col as T) bypasses collection key validation; change resolveCollections() (and any path using toIndex/from toValue) to preserve the narrower T[] type so callers can pass a properly typed collection key instead of asserting. Concretely, update resolveCollections() to return T[] (e.g., coerce the result of toValue(collection) to T[] once and use that typed array wherever queryCollection is called), and remove the inline `as T` cast in queryCollection calls so the compiler enforces the correct keyof PageCollections type at call sites.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/content/blog/studio-oss.md`:
- Line 71: Update the sentence "The modern Notion-like editing experience for
Markdown content is back with a improved version, powered by
[TipTap](https://tiptap.dev/) integrated through the [Nuxt UI
Editor](https://ui.nuxt.com/components/editor) component:" to fix the article
agreement typo by changing "a improved version" to "an improved version" so it
reads "...is back with an improved version..."; locate the exact string in the
document and correct the article.
In `@src/runtime/client.ts`:
- Around line 93-98: The search function can trigger concurrent init() calls
because multiple callers see !db and start initialization; modify search to wait
on a shared initPromise: if db is not ready, ensure you assign/initiate a single
shared initPromise (create/initPromise when missing) and await that promise
before calling queryFTS; use the existing initPromise and init() symbols (and
db) so subsequent concurrent search() callers await the same initialization
instead of starting duplicate adapters/index builds.
- Line 55: The current early-return when collections.length is zero returns
initPromise ?? Promise.resolve(db!), which can resolve to undefined if db isn't
initialized; update the branch so it never resolves to undefined: if initPromise
exists return it, otherwise if db is defined return Promise.resolve(db as
DatabaseAdapter), else return Promise.reject(new Error("DatabaseAdapter not
initialized")) (or trigger the existing initialization path) — adjust references
to collections, initPromise and db to implement this safe behavior.
---
Nitpick comments:
In `@docs/package.json`:
- Line 17: The package.json currently lists "drizzle-kit" in dependencies; move
"drizzle-kit" into devDependencies instead because it's a CLI/build tool not
required at runtime: remove the "drizzle-kit" entry from the top-level
"dependencies" and add the same version string under "devDependencies" (preserve
the version "^0.31.10"), then update your lockfile by running your package
manager (npm/yarn/pnpm install) to reflect the change; verify no runtime imports
reference "drizzle-kit" and commit the modified package.json and updated
lockfile.
In `@src/runtime/client.ts`:
- Line 100: Add a cleanup method to the composable that disposes the
DatabaseAdapter and resets internal state: implement a destroy or cleanup
function that, if the underlying DatabaseAdapter supports a close/closeAsync
method, calls it, then sets db = undefined, initPromise = undefined, indexedFor
= [], and status.value = 'idle'; finally include this method in the returned
object alongside status, search, and init so callers can explicitly release
resources.
- Line 74: The cast `col as T` in the line calling queryCollection(col as T)
bypasses collection key validation; change resolveCollections() (and any path
using toIndex/from toValue) to preserve the narrower T[] type so callers can
pass a properly typed collection key instead of asserting. Concretely, update
resolveCollections() to return T[] (e.g., coerce the result of
toValue(collection) to T[] once and use that typed array wherever
queryCollection is called), and remove the inline `as T` cast in queryCollection
calls so the compiler enforces the correct keyof PageCollections type at call
sites.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 15a5f26c-4253-43e8-a332-13bd471f5702
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (13)
docs/content/blog/studio-oss.mddocs/content/docs/4.utils/5.use-search-collection.mddocs/content/docs/8.advanced/1.fulltext-search.mddocs/package.jsonplayground/app.vueplayground/content.config.tsplayground/nuxt.config.tsplayground/package.jsonplayground/pages/search.vuesrc/module.tssrc/runtime/client.tssrc/runtime/internal/search.tstest/unit/searchCollection.test.ts
💤 Files with no reviewable changes (2)
- playground/package.json
- playground/nuxt.config.ts
🔗 Linked issue
❓ Type of change
📚 Description
Adds
useSearchCollection, a client-side composable that provides full-text search powered by SQLite FTS5. it builds an inverted index from content sections at runtime and queries it with BM25 ranking, prefix matching, and multi-column snippets. no external dependencies needed.Unified FTS5 table across multiple collections, lazy index building with
immediate: falseoption, prefix matching on all terms (typingcompomatches "composable"), configurable field restriction, BM25 column weights with heading-level boosting, camelCase title normalization for better ranking, and graceful handling of FTS5 syntax errors.queryCollectionSearchSectionsstill available for Fuse.js/MiniSearch users who need typo tolerance.📝 Checklist