# WasmEpubService

- File ID: file:hiutf5:wasmepubservice-cs
- Path: NexusReader.Web.Client/Services/WasmEpubService.cs
- Role: service
- Layer: infrastructure

## Purpose

Provides WebAssembly/client-side implementations for reading EPUB content and extracting EPUB metadata in-browser using HttpClient and a local EPUB library.

## Key Details

GetEpubContentAsync performs an HTTP GET to /api/epub/{ebookId}/{chapterIndex} and handles JSON deserialization and error paths; metadata extraction is performed locally via VersOne.Epub and returns LocalEpubMetadata; both methods use try/catch and FluentResults for uniform success/failure handling. Comment notes metadata extraction was moved to WasmEpubMetadataExtractor (line 40).

## Symbols

- WasmEpubReader: Provides WebAssembly/client-side implementations for reading EPUB content and extracting EPUB metadata in-browser using HttpClient and a local EPUB library.
- WasmEpubReader(): Constructor that stores the injected HttpClient for later HTTP calls.
- GetEpubContentAsync(): Fetches a chapter's reader page from the server API and returns a Result-wrapped ReaderPageViewModel, handling deserialization and errors.
- ExtractMetadataAsync(): Opens an EPUB stream locally, extracts title, author, and cover image, and returns them in a LocalEpubMetadata wrapped in a Result.
- WasmEpubReader: Client-side implementation of IEpubReader that fetches EPUB content from a server API using HttpClient.
- WasmEpubMetadataExtractor: Local EPUB metadata extractor that uses VersOne.Epub to read title, author and cover image from an EPUB stream.

## Dependencies

- No resolved dependencies.

## Calls

- No resolved calls.

## Unresolved References

- dependency-target: NexusReader.Application.Abstractions.Services (IEpubReader, IEpubMetadataExtractor referenced)
- dependency-target: NexusReader.Application.Queries.Reader (ReaderPageViewModel referenced)
- dependency-target: VersOne.Epub (used for local EPUB parsing)
- dependency-target: FluentResults (Result/Error used)
- interaction-target: Calls _httpClient.GetAsync($"/api/epub/{ebookId}/{chapterIndex}", cancellationToken) to retrieve chapter content.
- interaction-target: response.Content.ReadFromJsonAsync<ReaderPageViewModel>(cancellationToken: cancellationToken)
- interaction-target: response.Content.ReadAsStringAsync(cancellationToken)
- interaction-target: VersOne.Epub.EpubReader.OpenBookAsync(epubStream) to obtain book reference.
- interaction-target: bookRef.ReadCoverAsync()

## Coverage

- Symbols: 6
- Executable symbols: 3
- Executable symbols with control flow: 3
- Grounded edges: 6
- Unresolved references: 9
- Control-flow fragments: 3
- Unresolved jumps: 0
- Unsupported constructs: 0
- Control-flow coverage: 100%
- Required diagrams: 2
- Generated diagrams: 4

## Control Flow Fragments

- WasmEpubReader: 3 nodes, 2 edges, 0 jumps, 0 unsupported constructs
- GetEpubContentAsync: 27 nodes, 29 edges, 0 jumps, 0 unsupported constructs
- ExtractMetadataAsync: 13 nodes, 12 edges, 0 jumps, 0 unsupported constructs

## Diagrams

### WasmEpubService Neighborhood

```mermaid
flowchart LR
    class_veazt0_wasmepubreader("WasmEpubReader")
    file_hiutf5_wasmepubservice_cs("WasmEpubService")
    method_10bpn0_getepubcontentasync[["GetEpubContentAsync()"]]
    method_10zr37d_wasmepubreader[["WasmEpubReader()"]]
    method_1do3jf2_extractmetadataasync[["ExtractMetadataAsync()"]]
    type_5mx9m5_wasmepubmetadataextractor[/"WasmEpubMetadataExtractor"/]
    type_uf4ds6_wasmepubreader[/"WasmEpubReader"/]
```

### WasmEpubService Execution Overview

```mermaid
flowchart TD
    class_veazt0_wasmepubreader("WasmEpubReader")
    method_10bpn0_getepubcontentasync[["GetEpubContentAsync()"]]
    method_10zr37d_wasmepubreader[["WasmEpubReader()"]]
    method_1do3jf2_extractmetadataasync[["ExtractMetadataAsync()"]]
    unresolved_unresolved_155j8sa_interaction_target{"Unresolved: bookRef.ReadCoverAsync()"}
    unresolved_unresolved_ds2njs_interaction_target{"Unresolved: response.Content.ReadAsStringAsync(cancellationToken)"}
    unresolved_unresolved_i7268p_interaction_target{"Unresolved: VersOne.Epub.EpubReader.OpenBookAsync(epubStream) to obtain book reference."}
    unresolved_unresolved_ubjob5_interaction_target{"Unresolved: response.Content.ReadFromJsonAsync<ReaderPageViewModel>(cancellationToken: cancellationToken)"}
    unresolved_unresolved_y49oul_interaction_target{"Unresolved: Calls _httpClient.GetAsync($\\"/api/epub/{ebookId}/{chapterIndex}\\", cancellationToken) to retrieve chapter content."}
    class_veazt0_wasmepubreader -->|contains| method_10bpn0_getepubcontentasync
    class_veazt0_wasmepubreader -->|contains| method_10zr37d_wasmepubreader
    class_veazt0_wasmepubreader -->|contains| method_1do3jf2_extractmetadataasync
    class_veazt0_wasmepubreader -->|unresolved interaction| unresolved_unresolved_155j8sa_interaction_target
    class_veazt0_wasmepubreader -->|unresolved interaction| unresolved_unresolved_ds2njs_interaction_target
    class_veazt0_wasmepubreader -->|unresolved interaction| unresolved_unresolved_i7268p_interaction_target
    class_veazt0_wasmepubreader -->|unresolved interaction| unresolved_unresolved_ubjob5_interaction_target
    class_veazt0_wasmepubreader -->|unresolved interaction| unresolved_unresolved_y49oul_interaction_target
```

### GetEpubContentAsync() Control Flow

_Warnings: diagram method_10bpn0_getepubcontentasync__fragment_diagram exceeds preferred complexity thresholds (27 nodes, 29 edges)._

```mermaid
---
config:
  flowchart:
    defaultRenderer: "elk"
---
flowchart TD
    method_10bpn0_getepubcontentasync__action_23_getepubcontentasync_try_23_0__try("Begins protected execution")
    method_10bpn0_getepubcontentasync__action_23_try_catch_23__linear("Network/deserialization protection")
    method_10bpn0_getepubcontentasync__action_25_getepubcontentasync_await_25_1__await[["Waits for async work"]]
    method_10bpn0_getepubcontentasync__action_26_branch_26__condition{"response.IsSuccessStatusCode"}
    method_10bpn0_getepubcontentasync__action_26_branch_26__false("failure -> read error body (line 32) and return Fail with status and body (line 33)")
    method_10bpn0_getepubcontentasync__action_26_branch_26__merge("merge")
    method_10bpn0_getepubcontentasync__action_26_branch_26__true("success -> deserialize JSON to ReaderPageViewModel (line 28) and return Ok if not null (line 29)")
    method_10bpn0_getepubcontentasync__action_26_getepubcontentasync_branch_26_2__condition{"response.IsSuccessStatusCode"}
    method_10bpn0_getepubcontentasync__action_26_getepubcontentasync_branch_26_2__false("false")
    method_10bpn0_getepubcontentasync__action_26_getepubcontentasync_branch_26_2__merge("merge")
    method_10bpn0_getepubcontentasync__action_26_getepubcontentasync_branch_26_2__true("true")
    method_10bpn0_getepubcontentasync__action_28_getepubcontentasync_await_28_3__await[["Waits for async work"]]
    method_10bpn0_getepubcontentasync__action_29_guard_clause_29__condition{"viewModel != null"}
    method_10bpn0_getepubcontentasync__action_29_guard_clause_29__false("return Result.Fail(\\"Failed to deserialize response.\\")")
    method_10bpn0_getepubcontentasync__action_29_guard_clause_29__merge("merge")
    method_10bpn0_getepubcontentasync__action_29_guard_clause_29__true("return Result.Ok(viewModel)")
    method_10bpn0_getepubcontentasync__action_32_getepubcontentasync_await_32_5__await[["Waits for async work"]]
    method_10bpn0_getepubcontentasync__action_35_getepubcontentasync_catch_35_7__catch("catch Exception ex")
    method_10bpn0_getepubcontentasync__entry(["GetEpubContentAsync entry"])
    method_10bpn0_getepubcontentasync__action_25_external_call_25__linear("HTTP GET to server EPUB endpoint")
    method_10bpn0_getepubcontentasync__action_28_external_call_28__linear("Deserialize JSON response")
    method_10bpn0_getepubcontentasync__action_32_external_call_32__linear("Read error response body")
    method_10bpn0_getepubcontentasync__action_29_getepubcontentasync_return_29_4__return(["Returns result"])
    method_10bpn0_getepubcontentasync__action_33_getepubcontentasync_return_33_6__return(["Returns result"])
    method_10bpn0_getepubcontentasync__action_33_return_33__return(["Return failure Result for HTTP error"])
    method_10bpn0_getepubcontentasync__action_37_getepubcontentasync_return_37_8__return(["Returns result"])
    method_10bpn0_getepubcontentasync__action_37_return_37__return(["Return failure Result for exception"])
    method_10bpn0_getepubcontentasync__action_23_getepubcontentasync_try_23_0__try -->|next| method_10bpn0_getepubcontentasync__action_25_getepubcontentasync_await_25_1__await
    method_10bpn0_getepubcontentasync__action_23_try_catch_23__linear -->|next| method_10bpn0_getepubcontentasync__action_23_getepubcontentasync_try_23_0__try
    method_10bpn0_getepubcontentasync__action_25_external_call_25__linear -->|next| method_10bpn0_getepubcontentasync__action_26_branch_26__condition
    method_10bpn0_getepubcontentasync__action_25_getepubcontentasync_await_25_1__await -->|next| method_10bpn0_getepubcontentasync__action_25_external_call_25__linear
    method_10bpn0_getepubcontentasync__action_26_branch_26__condition -->|failure -> read error body line 32 and return Fail with status and body line 33| method_10bpn0_getepubcontentasync__action_26_branch_26__false
    method_10bpn0_getepubcontentasync__action_26_branch_26__condition -->|success -> deserialize JSON to ReaderPageViewModel line 28 and return Ok if not null line 29| method_10bpn0_getepubcontentasync__action_26_branch_26__true
    method_10bpn0_getepubcontentasync__action_26_branch_26__false -->|next| method_10bpn0_getepubcontentasync__action_26_branch_26__merge
    method_10bpn0_getepubcontentasync__action_26_branch_26__merge -->|next| method_10bpn0_getepubcontentasync__action_26_getepubcontentasync_branch_26_2__condition
    method_10bpn0_getepubcontentasync__action_26_branch_26__true -->|next| method_10bpn0_getepubcontentasync__action_26_branch_26__merge
    method_10bpn0_getepubcontentasync__action_26_getepubcontentasync_branch_26_2__condition -->|false| method_10bpn0_getepubcontentasync__action_26_getepubcontentasync_branch_26_2__false
    method_10bpn0_getepubcontentasync__action_26_getepubcontentasync_branch_26_2__condition -->|true| method_10bpn0_getepubcontentasync__action_26_getepubcontentasync_branch_26_2__true
    method_10bpn0_getepubcontentasync__action_26_getepubcontentasync_branch_26_2__false -->|next| method_10bpn0_getepubcontentasync__action_26_getepubcontentasync_branch_26_2__merge
    method_10bpn0_getepubcontentasync__action_26_getepubcontentasync_branch_26_2__merge -->|next| method_10bpn0_getepubcontentasync__action_28_getepubcontentasync_await_28_3__await
    method_10bpn0_getepubcontentasync__action_26_getepubcontentasync_branch_26_2__true -->|next| method_10bpn0_getepubcontentasync__action_26_getepubcontentasync_branch_26_2__merge
    method_10bpn0_getepubcontentasync__action_28_external_call_28__linear -->|return| method_10bpn0_getepubcontentasync__action_29_getepubcontentasync_return_29_4__return
    method_10bpn0_getepubcontentasync__action_28_getepubcontentasync_await_28_3__await -->|next| method_10bpn0_getepubcontentasync__action_28_external_call_28__linear
    method_10bpn0_getepubcontentasync__action_29_getepubcontentasync_return_29_4__return -->|next| method_10bpn0_getepubcontentasync__action_29_guard_clause_29__condition
    method_10bpn0_getepubcontentasync__action_29_guard_clause_29__condition -->|return Result.Fail Failed to deserialize response.| method_10bpn0_getepubcontentasync__action_29_guard_clause_29__false
    method_10bpn0_getepubcontentasync__action_29_guard_clause_29__condition -->|return Result.Ok viewModel| method_10bpn0_getepubcontentasync__action_29_guard_clause_29__true
    method_10bpn0_getepubcontentasync__action_29_guard_clause_29__false -->|next| method_10bpn0_getepubcontentasync__action_29_guard_clause_29__merge
    method_10bpn0_getepubcontentasync__action_29_guard_clause_29__merge -->|next| method_10bpn0_getepubcontentasync__action_32_getepubcontentasync_await_32_5__await
    method_10bpn0_getepubcontentasync__action_29_guard_clause_29__true -->|next| method_10bpn0_getepubcontentasync__action_29_guard_clause_29__merge
    method_10bpn0_getepubcontentasync__action_32_external_call_32__linear -->|return| method_10bpn0_getepubcontentasync__action_33_getepubcontentasync_return_33_6__return
    method_10bpn0_getepubcontentasync__action_32_getepubcontentasync_await_32_5__await -->|next| method_10bpn0_getepubcontentasync__action_32_external_call_32__linear
    method_10bpn0_getepubcontentasync__action_33_getepubcontentasync_return_33_6__return -->|return| method_10bpn0_getepubcontentasync__action_33_return_33__return
    method_10bpn0_getepubcontentasync__action_33_return_33__return -->|exception| method_10bpn0_getepubcontentasync__action_35_getepubcontentasync_catch_35_7__catch
    method_10bpn0_getepubcontentasync__action_35_getepubcontentasync_catch_35_7__catch -->|return| method_10bpn0_getepubcontentasync__action_37_getepubcontentasync_return_37_8__return
    method_10bpn0_getepubcontentasync__action_37_getepubcontentasync_return_37_8__return -->|return| method_10bpn0_getepubcontentasync__action_37_return_37__return
    method_10bpn0_getepubcontentasync__entry -->|next| method_10bpn0_getepubcontentasync__action_23_try_catch_23__linear
```

### ExtractMetadataAsync() Control Flow

```mermaid
flowchart TD
    method_1do3jf2_extractmetadataasync__action_47_extractmetadataasync_try_47_0__try("Begins protected execution")
    method_1do3jf2_extractmetadataasync__action_47_try_catch_47__linear("Local EPUB parsing protection")
    method_1do3jf2_extractmetadataasync__action_49_extractmetadataasync_await_49_1__await[["Waits for async work"]]
    method_1do3jf2_extractmetadataasync__action_50_assignment_50__linear("Normalize title and author with fallbacks")
    method_1do3jf2_extractmetadataasync__action_52_extractmetadataasync_await_52_2__await[["Waits for async work"]]
    method_1do3jf2_extractmetadataasync__action_55_extractmetadataasync_catch_55_4__catch("catch Exception ex")
    method_1do3jf2_extractmetadataasync__entry(["ExtractMetadataAsync entry"])
    method_1do3jf2_extractmetadataasync__action_49_external_call_49__linear("Open EPUB book asynchronously")
    method_1do3jf2_extractmetadataasync__action_52_external_call_52__linear("Read cover image bytes")
    method_1do3jf2_extractmetadataasync__action_53_extractmetadataasync_return_53_3__return(["Returns result"])
    method_1do3jf2_extractmetadataasync__action_53_return_53__return(["Return success Result with LocalEpubMetadata"])
    method_1do3jf2_extractmetadataasync__action_57_extractmetadataasync_return_57_5__return(["Returns result"])
    method_1do3jf2_extractmetadataasync__action_57_return_57__return(["Return failure Result for exception"])
    method_1do3jf2_extractmetadataasync__action_47_extractmetadataasync_try_47_0__try -->|next| method_1do3jf2_extractmetadataasync__action_49_extractmetadataasync_await_49_1__await
    method_1do3jf2_extractmetadataasync__action_47_try_catch_47__linear -->|next| method_1do3jf2_extractmetadataasync__action_47_extractmetadataasync_try_47_0__try
    method_1do3jf2_extractmetadataasync__action_49_external_call_49__linear -->|next| method_1do3jf2_extractmetadataasync__action_50_assignment_50__linear
    method_1do3jf2_extractmetadataasync__action_49_extractmetadataasync_await_49_1__await -->|next| method_1do3jf2_extractmetadataasync__action_49_external_call_49__linear
    method_1do3jf2_extractmetadataasync__action_50_assignment_50__linear -->|next| method_1do3jf2_extractmetadataasync__action_52_extractmetadataasync_await_52_2__await
    method_1do3jf2_extractmetadataasync__action_52_external_call_52__linear -->|return| method_1do3jf2_extractmetadataasync__action_53_extractmetadataasync_return_53_3__return
    method_1do3jf2_extractmetadataasync__action_52_extractmetadataasync_await_52_2__await -->|next| method_1do3jf2_extractmetadataasync__action_52_external_call_52__linear
    method_1do3jf2_extractmetadataasync__action_53_extractmetadataasync_return_53_3__return -->|return| method_1do3jf2_extractmetadataasync__action_53_return_53__return
    method_1do3jf2_extractmetadataasync__action_53_return_53__return -->|exception| method_1do3jf2_extractmetadataasync__action_55_extractmetadataasync_catch_55_4__catch
    method_1do3jf2_extractmetadataasync__action_55_extractmetadataasync_catch_55_4__catch -->|return| method_1do3jf2_extractmetadataasync__action_57_extractmetadataasync_return_57_5__return
    method_1do3jf2_extractmetadataasync__action_57_extractmetadataasync_return_57_5__return -->|return| method_1do3jf2_extractmetadataasync__action_57_return_57__return
    method_1do3jf2_extractmetadataasync__entry -->|next| method_1do3jf2_extractmetadataasync__action_47_try_catch_47__linear
```

## Diagram Validation

- Status: warn
- Warning: control-flow: diagram method_10bpn0_getepubcontentasync__fragment_diagram exceeds preferred complexity thresholds (27 nodes, 29 edges).
