feat(nav): honor frontmatter order and description, add prev/next links - #129
Merged
Merged
Conversation
Navigation was derived entirely from the database with no ordering controls: articles sorted alphabetically by title, folders appeared in whatever order their first article happened to sort into, and every page's meta description was its own title repeated because libsql-search parses frontmatter description into the embedding text and then discards it. Two nullable columns now carry sort_order and description, added by a PRAGMA-guarded ALTER so existing deployments upgrade without a manual migration, populated by a post-index pass and read through one query. Folder sequence lives in a single config file. Article pages emit the real description and prev/next links within their folder.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #129 +/- ##
==========================================
+ Coverage 94.43% 95.09% +0.65%
==========================================
Files 19 22 +3
Lines 539 611 +72
Branches 144 170 +26
==========================================
+ Hits 509 581 +72
Misses 12 12
Partials 18 18 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
codecov/patch and codecov/project flagged the catch that translates a missing-column error into one naming the commands to run. Added tests against a real table in the stock libsql-search shape asserting the message names both commands, that the underlying error is kept as the cause, and that an unrelated error is rethrown unchanged rather than relabelled. File line coverage goes from 83.33% to 100%.
codecov/patch reported no missing lines but seven partial branches. Chasing them found that parseOrder's bigint arm cannot execute — the libSQL client's default intMode throws on an integer too wide for a JS number rather than returning a bigint, and SQLite stores neither NaN nor Infinity, so the finite check was unreachable too. The comment claiming the column arrives as number or bigint was wrong. That branch is removed rather than tested. The remaining arms are now covered: tags degrading to an empty array for unparseable JSON, a JSON scalar, and NULL; a whitespace-only description reading as absent; articles with no folder grouping under the root folder as neighbors of each other; an unknown slug yielding no neighbors; and unordered-versus-ordered comparison in both directions.
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
descriptionwas discarded, so every meta description andog:descriptionwas the title repeated. There was also no prev/next navigation within a folder.sort_orderanddescription) now persist this data in the search table, populated by a post-index pass and read through one explicit-column query, with folder sequence controlled from a single config file.Problem
Four separate gaps: alphabetical article order with no
orderfrontmatter, incidental folder order (whatever order a folder's first article happened to sort into),descriptiondiscarded by libsql-search so every<meta name="description">andog:descriptionwas the title repeated, and no prev/next links.Where the data lives
The libsql-search table has no
descriptionorordercolumn, so this project persists both. Two nullable columns (sort_order—orderis a SQL keyword — anddescription) are written by a post-index pass that is a sibling of the existing keyword-index rebuild, and read by one query that selects an explicit column list rather thanSELECT *so article bodies are not dragged into the build. Nothing reads./contentat render time, which keeps one source of truth and works where content is not colocated with the server.Upgrade path
CREATE TABLE IF NOT EXISTSwill not add a column to an existing table, soensureNavColumnsdoes aPRAGMA table_infocheck plusALTER TABLE ADD COLUMN. Verified against three real pre-existing database states: a populated pre-migration table, anindex:localrun with no priordb:init, and the repo's ownlocal.dbcreated before this branch. Idempotent, handles a partial earlier migration, and throws rather than silently no-op'ing when the table is absent.Sort semantics
Within a folder,
orderascending, then title, then slug. Slug is UNIQUE so the comparator is a total order and builds are deterministic. An article withoutordersorts after every article that has one;order: 0counts as declared, not absent.localeCompareis pinned to'en'so a build machine's locale cannot change page order. Folders followsrc/config/nav.ts; unlisted folders sort after listed ones, alphabetically, and listing every folder is not required.Fail-loud on a stale database
The new query selects the new columns, so a database that never ran
db:init/indexnow fails at build time rather than serving pages in the wrong order. All three page routes are prerendered and neither API endpoint touches these columns, so there is no runtime path that can 500 — a stale deployment simply cannot be built. The error now names the commands to run instead of only the missing column.Review findings applied
gray-mattermoved from devDependencies to dependencies — it was imported by a shipped script, andpnpm install --prodleaves it unresolvable at the project root, sopnpm install --prod && pnpm indexon a deploy host would crash withERR_MODULE_NOT_FOUND. Being a transitive dependency of libsql-search makes this worse rather than better under pnpm's strict layout, since it is symlinked only into that package's own tree. The move costs three lockfile lines and no new package.Test gaps closed
A test that asserted the old files-read count is inverted to assert rows matched. The shipped folder configuration is now pinned — proven load-bearing, emptying
src/config/nav.tsfails it, where previously the whole suite passed.Verification
428 tests pass, lint and
tsc --noEmitclean, credential-free build and smoke pass. Built output confirms the sidebar renders Getting Started → Features → Theme where alphabetical would give Features first, and both meta description tags carry the real frontmatter text.Worth knowing
Prev/next does not appear in the shipped demo because each of the three folders holds a single article. All four boundary cases were verified with temporary fixtures — first has no prev, last has no next, single-article folder emits no nav element, and links never cross a folder boundary — then the fixtures were removed. Shipping a second article in one folder would make the feature visible to anyone evaluating the theme.
Closes #106