feat: Blazor WASM counter sample + fix BLite.Wasm TFM for project references#64
Conversation
Agent-Logs-Url: https://github.com/EntglDb/BLite/sessions/335a7711-1dce-4bd1-840f-6a52bdd7b267 Co-authored-by: mrdevrobot <12503462+mrdevrobot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a new end-to-end Blazor WebAssembly sample to demonstrate BLite persistence in-browser, and adjusts BLite.Wasm to use a project-reference-friendly TFM while preserving browser-only API annotation.
Changes:
- Add
samples/BLite.BlazorWasm/: a minimal Blazor WASM “persistent counter” app that usesBLiteWasmwith OPFS → IndexedDB → in-memory fallback. - Change
BLite.WasmTFM fromnet10.0-browsertonet10.0, adding an assembly-level[SupportedOSPlatform("browser")]to keep the browser-only constraint. - Update
WASM_SUPPORT.mdand include the new sample project inBLite.slnx.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/BLite.Wasm/BrowserPlatform.cs | Marks the assembly as browser-only via SupportedOSPlatform("browser"). |
| src/BLite.Wasm/BLite.Wasm.csproj | Switches TFM to net10.0 to avoid project-reference restore issues. |
| samples/BLite.BlazorWasm/wwwroot/index.html | Adds the Blazor WASM host page for the sample. |
| samples/BLite.BlazorWasm/wwwroot/css/app.css | Adds sample styling (loading/error UI + layout). |
| samples/BLite.BlazorWasm/wwwroot/blite-opfs.mjs | Provides OPFS JS module required by BLite.Wasm [JSImport]. |
| samples/BLite.BlazorWasm/wwwroot/blite-indexeddb.mjs | Provides IndexedDB (+ WAL) JS module required by BLite.Wasm [JSImport]. |
| samples/BLite.BlazorWasm/_Imports.razor | Sets up common Razor imports for the sample. |
| samples/BLite.BlazorWasm/Services/CounterStore.cs | Implements the persisted counter service backed by BLite browser storage. |
| samples/BLite.BlazorWasm/Program.cs | Wires up the Blazor WASM app and registers CounterStore. |
| samples/BLite.BlazorWasm/Pages/NotFound.razor | Adds a not-found page for the router. |
| samples/BLite.BlazorWasm/Pages/Home.razor | Adds landing page describing the demo + quick-start snippet. |
| samples/BLite.BlazorWasm/Pages/Counter.razor | Adds the persistent counter UI and status display. |
| samples/BLite.BlazorWasm/Layout/NavMenu.razor.css | Adds nav menu styling. |
| samples/BLite.BlazorWasm/Layout/NavMenu.razor | Adds navigation links (Home/Counter). |
| samples/BLite.BlazorWasm/Layout/MainLayout.razor.css | Adds main layout styling. |
| samples/BLite.BlazorWasm/Layout/MainLayout.razor | Adds main layout structure and GitHub link. |
| samples/BLite.BlazorWasm/BrowserPlatform.cs | Marks the sample assembly as browser-only via SupportedOSPlatform("browser"). |
| samples/BLite.BlazorWasm/BLite.BlazorWasm.csproj | Introduces the sample project + references BLite.Wasm and ensures JS modules are present. |
| samples/BLite.BlazorWasm/App.razor | Adds the app router and default layout wiring. |
| WASM_SUPPORT.md | Marks Issue 5 as done and documents how to run the sample. |
| BLite.slnx | Adds the new sample project to the solution. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public async Task InitializeAsync() | ||
| { | ||
| if (IsInitialized) | ||
| return; | ||
|
|
||
| try | ||
| { | ||
| // Detect the backend before creating the engine so we can display it. | ||
| Backend = BLiteWasm.DetectBestBackend(); | ||
| _engine = await BLiteWasm.CreateAsync("blite-counter-demo", Backend); | ||
|
|
||
| // Try to restore the previously saved counter value. | ||
| await foreach (var doc in _engine.FindAllAsync(CollectionName)) | ||
| { | ||
| if (doc.TryGetString("name", out var name) && name == CounterName) | ||
| { | ||
| doc.TryGetInt32("value", out var saved); | ||
| Value = saved; | ||
|
|
||
| if (doc.TryGetId(out var id)) | ||
| _documentId = id; | ||
|
|
||
| if (doc.TryGetValue("updated_at", out var tsVal) && tsVal.IsInt64) | ||
| LastSaved = DateTimeOffset.FromUnixTimeMilliseconds(tsVal.AsInt64); | ||
|
|
||
| break; | ||
| } | ||
| } | ||
|
|
||
| IsInitialized = true; | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| Error = $"BLite initialisation failed: {ex.Message}"; | ||
| } |
There was a problem hiding this comment.
InitializeAsync() sets Error on failure but never clears it on a subsequent successful initialization. Because IsInitialized stays false after a failure, retries are possible, but the UI will keep showing the old error unless Error is reset on success (or at the start of initialization).
| _engine = await BLiteWasm.CreateAsync("blite-counter-demo", Backend); | ||
|
|
||
| // Try to restore the previously saved counter value. | ||
| await foreach (var doc in _engine.FindAllAsync(CollectionName)) |
There was a problem hiding this comment.
This async method dereferences the nullable field _engine after await points (e.g., await foreach), which will typically produce nullable warnings and makes the code harder to reason about. Consider using a local non-null engine variable (capture the result of CreateAsync into a local, then assign the field) and use that local for the rest of the method.
| _engine = await BLiteWasm.CreateAsync("blite-counter-demo", Backend); | |
| // Try to restore the previously saved counter value. | |
| await foreach (var doc in _engine.FindAllAsync(CollectionName)) | |
| var engine = await BLiteWasm.CreateAsync("blite-counter-demo", Backend); | |
| _engine = engine; | |
| // Try to restore the previously saved counter value. | |
| await foreach (var doc in engine.FindAllAsync(CollectionName)) |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <title>BLite WASM Demo</title> | ||
| <base href="/" /> | ||
| <link rel="preload" id="webassembly" /> |
There was a problem hiding this comment.
<link rel="preload" id="webassembly" /> is missing the required attributes (at least href and as). As written it won't preload anything and may trigger HTML validation / browser console warnings; either remove it or specify the actual resource and as type you intend to preload.
| <link rel="preload" id="webassembly" /> |
Addresses Issue 5 from
WASM_SUPPORT.md: adds an end-to-end Blazor WebAssembly sample demonstrating BLite persistence in the browser via a counter app.Sample —
samples/BLite.BlazorWasm/Minimal Blazor WASM app with a counter that survives page reloads:
Services/CounterStore.cs— singleton that callsBLiteWasm.CreateAsync("blite-counter-demo"), auto-selects storage backend (OPFS → IndexedDB → in-memory), and upserts a single counter document on every mutationPages/Counter.razor— increment / decrement / reset UI; displays active backend, last-saved timestamp, and persistence noticePages/Home.razor— landing page with quick-start snippetwwwroot/blite-indexeddb.mjs/blite-opfs.mjs— JS interop modules (copied fromBLite.Wasm/wwwroot) required by[JSImport]at runtimedotnet run --project samples/BLite.BlazorWasm/BLite.BlazorWasm.csproj # open https://localhost:5001/counterBLite.Wasm TFM fix
net10.0-browser→net10.0— Blazor WASM projects targetnet10.0(not-browser), so the old TFM produced aNU1201restore error on project references. A newBrowserPlatform.csadds[assembly: SupportedOSPlatform("browser")]to preserve the browser-only constraint that the old TFM provided implicitly.Docs
WASM_SUPPORT.md— Issue 5 marked[Done]; running instructions added.