b84a52f286
Rewrite SearchService.searchPage into ONE engine for web-UI, MCP agent and share: - A2 server-side query parser (search-query-parser.ts): quote-aware tokenizer, leading +/- operators (internal -,.,: literal), "phrase", metachar-stripped bare terms; tsquery built as a parameterized AST via SQL ||/&&/!! (never string-concat). only-negation/empty short-circuit. - A3 match=auto routes identifier-like terms (10.31.41, esp32, WB-MGE-30D86B) to the substring/trigram branch, words to FTS; word/ prefix/substring overrides. - A4 RRF (k=60) fuses the FTS branch (ts_rank_cd) and substring branch (title-exact>title-sub>text tier) by RANK; ORDER BY rrf DESC, id. - A5 exact permission-filtered total (fail-closed via filterAccessiblePageIds with the #348 hasRestricted fast-path), CANDIDATE_CAP fusion window, offset/limit, hasMore, truncatedAtCap. - A6 single path (spaceId/share/creatorId/titleOnly/parentPageId/match); share uses getPageAndDescendantsExcludingRestricted. - A7 response superset per hit (id/pageId/slugId/icon/title/space/…/rank/ highlight/snippet/path/score/matchedFields/matchedTerms). - A8 buildAncestorPaths now skips deleted + cross-space ancestors. - A9 DTOs: match, offset, total/hasMore/truncatedAtCap/query/matchedFields. SEARCH_MODE=or|and toggles the parser; SEARCH_CANDIDATE_CAP tunes the window. Legacy lookup unit/int specs replaced by parser unit tests + a 13-criteria integration spec on real pg (incl. a permission mutation guard and a fail-closed propagation test). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>