fix(brain): give search an index to search - #196
Merged
Conversation
Reported: the content search always says no results. No error in the console — the worker receives the message and answers, with an empty array. Two causes, both of which had to be fixed. ## The content store was never created `idb-keyval`'s `createStore` opens the database **without a version** and creates only its own object store in the upgrade. Called twice for one database — `brain-meta` and `brain-content` — only the first store ever exists: by the time the second opens, the database is already at version 1, no upgrade fires, and every transaction against it throws `NotFoundError`. `brain-meta` is touched first, on `init`. So `brain-content` was never created, and the IndexedDB content cache has never worked. The app did not visibly suffer because the sync response is also kept in an in-memory map, which is what `queryContent` reads. The search index is the one consumer that reads from IndexedDB — so it was built over zero keys, every time. Both stores are now created in one upgrade, at an explicit version. Version 2 rather than 1 on purpose: a browser that has already visited Studio holds `cr-brain` at version 1 with only `brain-meta`, and opening at 1 would find it current and fire no upgrade at all. With a version comes upgrade blocking, so: `onversionchange` closes the connection instead of blocking another tab's upgrade, `onblocked` rejects instead of leaving reads pending forever, and a failed open is not cached. ## The index was built in exactly one place `rebuildSearchIndex` ran only after the "nothing changed" early return in `sync`. A worker is created fresh on every page load and its FlexSearch index lives only in memory, while the content it is built from lives in IndexedDB and survives. So on the common path — reload a project nobody has edited — the index was never built. It is now ensured in the delta branch and, belt and braces, before any search: every path that can leave a worker without an index ends up at one of the two rather than silently answering nothing. Neither of these was introduced by #193. Nothing called `searchContent` before it, so nothing noticed. The test drives the worker's own message handler over fake-indexeddb, loading it twice — once to populate the database as a real session would, once as the reloaded worker that has never synced. It fails against either bug alone.
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.
Search always said "no results", with no error — the worker received the message and answered with an empty array.
Two causes. Both had to be fixed; either one alone still leaves search empty.
1. The content store was never created
idb-keyval'screateStoreopens the database without a version and creates only its own object store in the upgrade. Called twice for one database —brain-metaandbrain-content— only the first store ever exists: by the time the second opens, the database is already at version 1, no upgrade fires, and every transaction against it throwsNotFoundError.brain-metais touched first, oninit. Sobrain-contentwas never created, and the IndexedDB content cache has never worked.The app did not visibly suffer because the sync response is also kept in an in-memory map, which is what
queryContentreads. The search index is the one consumer that reads from IndexedDB — so it was built over zero keys, every time.Both stores are created in one upgrade now, at an explicit version. Version 2, not 1: a browser that has already visited Studio holds
cr-brainat version 1 with onlybrain-meta, and opening at 1 would find it current and fire no upgrade at all.An explicit version brings upgrade blocking, so that is handled too:
onversionchangecloses the connection rather than blocking another tab's upgradeonblockedrejects rather than leaving every read pending forever — an error is visible and recoverable, a promise that never settles is neither2. The index was built in exactly one place
rebuildSearchIndexran only after the "nothing changed" early return insync. A worker is created fresh on every page load and its FlexSearch index lives only in memory, while the content it is built from lives in IndexedDB and survives.So on the common path — reload a project nobody has edited — the delta sync returns nothing, the early return fires, and the index is never built.
Ensured now in the delta branch, and again before any search. Every path that can leave a worker without an index reaches one of the two.
Neither was introduced by #193
Nothing in
app/calledsearchContentbefore it. The store bug predates the search feature entirely; #193 is just the first thing that read from IndexedDB.Verified
fake-indexeddb, loading it twice: once to populate the database as a real session would, once as the reloaded worker that has never synced. It reproduces the report exactly and fails against either bug alone — I checked both.pnpm lint0 errors ·pnpm typecheckclean ·pnpm test148 files / 1282 testsWorth knowing
The IndexedDB cache starts working now, for the first time. That is the intent — but it means the offline/instant-load path is genuinely exercised in a way it never has been, so it deserves a look in a browser rather than a shrug.
Not verified — needs a browser