fix(search): move scope→item-type taxonomy into Rust (UR-049, DR-063)
Stage 1 of scoped-search-boundary-implementation.md — the query side.
scoped-search-boundary.md diagnosed this leak, specified the fix in
detail, and became the justification for the boundary rule in CLAUDE.md,
the check:boundary tripwire, and the spec-review checklist. The fix was
never built: SCOPE_ITEM_TYPES was still live in searchScope.ts, called by
library.ts, and no SearchScope existed anywhere in src-tauri/. The rule's
own founding violation was still shipping.
Rust now owns the taxonomy:
pub enum SearchScope { All, Music, Movies, Tv }
impl SearchScope { pub fn item_types(self) -> Option<Vec<String>> }
- SearchOptions gains `scope`, resolved by resolve_scope(). Scope wins
over include_item_types, which stays for the non-search get_items
callers that legitimately request one concrete type.
- repository_search resolves the scope ONCE, before the cache/server
paths diverge, so online and offline filter identically — the failure
mode most likely to go unnoticed.
- All expands to None (no filter), not the union of the other scopes:
an explicit includeItemTypes list would silently drop People, folders,
and any type nobody enumerated.
- searchScope.ts re-exports SearchScope from generated bindings instead
of a hand-written union, and no longer names an item type for search.
- library.ts sends { scope }.
8 Rust tests written first, confirmed failing on "use of undeclared type
SearchScope" before the implementation existed.
The frontend tests that asserted includeItemTypes contents were rewritten
to assert the opaque scope is sent and includeItemTypes is absent —
keeping the old assertions would require the frontend to know the
taxonomy again, defeating the fix. The expansion is now asserted in Rust.
Verified the spec's headline criterion by hashing every src/ file, adding
"AudioBook" to the Music scope in Rust, and re-hashing: zero frontend
files change. That criterion failed before this commit.
Stage 2 (result-side grouping: GROUP_ITEM_TYPES, GroupedSearchResult on
both search payloads) remains open.
This commit is contained in:
+25
-2
@@ -1290,7 +1290,12 @@ async repositoryGetGenres(handle: string, parentId: string | null) : Promise<Gen
|
||||
return await TAURI_INVOKE("repository_get_genres", { handle, parentId });
|
||||
},
|
||||
/**
|
||||
* Search for items
|
||||
* Search for items.
|
||||
*
|
||||
* Resolves `SearchOptions::scope` into concrete Jellyfin item types before
|
||||
* dispatching, so scope taxonomy stays in Rust.
|
||||
*
|
||||
* TRACES: UR-049, UR-050 | DR-063
|
||||
*/
|
||||
async repositorySearch(handle: string, query: string, options: SearchOptions | null, requestId: number) : Promise<SearchResult> {
|
||||
return await TAURI_INVOKE("repository_search", { handle, query, options, requestId });
|
||||
@@ -2517,11 +2522,29 @@ failed: number }
|
||||
/**
|
||||
* Options for search queries
|
||||
*/
|
||||
export type SearchOptions = { limit?: number | null; includeItemTypes?: string[] | null; searchTerm?: string | null }
|
||||
export type SearchOptions = { limit?: number | null; includeItemTypes?: string[] | null; searchTerm?: string | null;
|
||||
/**
|
||||
* Opaque scope selected by the UI. When set it **wins** over
|
||||
* `include_item_types`, which remains for the non-search `get_items`
|
||||
* callers that legitimately request a single concrete type.
|
||||
*/
|
||||
scope?: SearchScope | null }
|
||||
/**
|
||||
* Search result with pagination
|
||||
*/
|
||||
export type SearchResult = { items: MediaItem[]; totalRecordCount: number }
|
||||
/**
|
||||
* An opaque search scope the frontend selects; Rust owns what it *means*.
|
||||
*
|
||||
* The expansion table below is Jellyfin domain vocabulary: it changes when
|
||||
* Jellyfin adds or renames an item type, never when the UI is redesigned. It
|
||||
* previously lived in the frontend (`searchScope.ts`), which is the boundary
|
||||
* leak documented in docs/specs/scoped-search-boundary.md. The frontend now
|
||||
* sends the enum and never names an item type in connection with search.
|
||||
*
|
||||
* TRACES: UR-049 | DR-063
|
||||
*/
|
||||
export type SearchScope = "all" | "music" | "movies" | "tv"
|
||||
/**
|
||||
* Security status info
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user