Skip to content

wasm-tryout: replace todo app with FlagdSchemaAbstraction port (multi-file, DB-backed, WASM + REST) - #5

Draft
DanielHabenicht with Copilot wants to merge 38 commits into
tryout-dotnet-wasmfrom
copilot/remove-todo-app-functionality-again
Draft

DanielHabenicht with Copilot wants to merge 38 commits into
tryout-dotnet-wasmfrom
copilot/remove-todo-app-functionality-again

Conversation

Copilot AI commented Feb 25, 2026 •

Copy link
Copy Markdown
Contributor

Removes the todo-app scaffold from wasm-tryout/Backend and replaces it with a C# port of the TypeScript FlagdSchemaAbstraction class, backed by SQLite/EF Core, with multi-file support, a [JSInvokable] service surface, and a REST-style controller layer.

Deleted

  • TodoItem.cs, TodoDbContext.cs, TodoJsonContext.cs, TodoService.cs

New EF Core schema

Three tables in a shared in-memory SQLite database (IndexedDB-persisted via raw byte export/import, same pattern as before):

  • flag_files — named schema documents
  • flag_entries — flags per file (type, state, valueJson, metadataJson, targetingJson); unique on (file_id, flag_key)
  • environment_entries — environment name + JSON-encoded aliases; unique on (file_id, name)

FlagdService ([JSInvokable])

Ports FlagdSchemaAbstraction.fromSchema() / exportSchema() and exposes full CRUD over the WASM boundary:

// Direct JS/WASM calls
Backend.FlagdService.createFile("production.json");
Backend.FlagdService.upsertFlag(fileId, JSON.stringify({ key: "dark-mode", type: "boolean", state: "ENABLED", valueJson: "true" }));
Backend.FlagdService.importSchema(fileId, rawFlagdSchemaJson);
const schema = Backend.FlagdService.exportSchema(fileId); // round-trips to flagd-compatible JSON

ImportSchema parses $evaluators for environment aliases (pattern isXxx) and reconstructs them on export — matching the TypeScript logic exactly.

FlagdController (REST routing layer)

Single [JSInvokable] entry point that dispatches by (method, path):

// Also exposed as window.api() for browser-console testing
window.api("GET",    "/api/files");
window.api("POST",   "/api/files/1/flags",        { key: "my-flag", type: "string", state: "ENABLED" });
window.api("POST",   "/api/files/1/schema",        rawFlagdSchemaJson);
window.api("GET",    "/api/files/1/schema");
window.api("DELETE", "/api/files/1/environments/production");

index.html

Replaced todo UI with a two-panel flagd schema manager: file list, flags table (create/edit/delete), environments table, schema import/export textarea, and IndexedDB persistence.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Co-authored-by: DanielHabenicht <13590797+DanielHabenicht@users.noreply.github.com>
Copilot AI changed the title [WIP] Remove todo app functionalities and port flagd schema wasm-tryout: replace todo app with FlagdSchemaAbstraction port (multi-file, DB-backed, WASM + REST) Feb 25, 2026
DanielHabenicht and others added 13 commits April 16, 2026 13:21
- Replace the Rust Dockerfile with a multi-stage .NET build: build the
  Bootsharp WASM package, the API (emits the OpenAPI doc) and the Angular
  UI, then serve both from an ASP.NET runtime image on port 3000.
- Serve the built UI from the API via static files + SPA fallback.
- Remove the Rust backend job from CI checks.
- Trim docker-compose to the .NET services (drop Azurite/Azure storage).
- Reuse scripts/setup-api-client.js for the client patch instead of a
  hand-written sed; it now applies patches when generation is skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes several gaps left by the .NET/collections rewrite that broke the
Playwright e2e tests, and updates the specs to the current UI:

Backend
- API wired FlagdService's context factory to the request-scoped DbContext,
  but the service disposes a context per call (`using var db`). The first
  call disposed the shared context, so any multi-call request (schema export)
  threw ObjectDisposedException. Give it a fresh FlagdDbContext per call.

Frontend
- Restore auto-navigation to a flags-file after create/import (navigate to
  /local/:collectionId from the CreateCollection/ImportSchema handlers).
- Wire the playground to the store: store the exported schema in a
  selectedSchema slice, refresh it after flag load/create/update/delete, and
  read it in the drawer's getSchema() (was stubbed to undefined).
- Persist the flag value on save (onSave dropped booleanValue/stringValue/
  numberValue/objectValue, so flags saved as null and failed schema export).
- Fix getFlagsFileRouteSegments() to use the current :uri/:collectionId route
  params (was reading removed fileName/name params, so Settings and routed
  edit navigation never fired).

E2E
- Update route assertions to /local/:collectionId, poll the playground
  re-evaluation instead of a fixed wait, and use .sidebar (not .project-list).

Dockerfile
- Move the wasm-tools/apt toolchain install before COPY src so source changes
  don't re-run it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Centralize the ExportSchema refresh into a single effect in the playground
  drawer keyed on the selected collection + its flags, instead of dispatching
  ExportSchema from each flag mutation handler (load/create/update/delete).
- Note in Program.cs that the per-call FlagdDbContext still uses SQLite via
  OnConfiguring (the switch off AddDbContext didn't change the provider).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make the backend a runtime concern (not the BACKEND_TYPE build flag) so the
in-browser wasm engine and REST servers coexist and are selectable per uri.

Backends
- Resolve the backend for each operation in flight from the selected server uri
  (no global "active backend"). RestBackendFactory hands out a RestFlagBackend
  bound to one base URL (cached per URL), so different servers can be talked to
  concurrently; wasm is a single lazily-booted in-browser engine.
- On startup, probe the same-origin API: reachable -> register "This Server"
  (baseUrl = window.location.origin) and use it; otherwise boot the in-browser
  wasm backend ("In Browser"). Wasm only boots when no server is present.
- Arbitrary remotes: Connect Backend adds a remote to the single servers map
  (persisted); "This Server" is an auto-detected entry refreshed on startup and
  never trusted from persistence. Stale/old-format persisted entries are dropped.
- Routes carry the server uri (:uri/:collectionId) so navigation picks the
  backend.

API
- CORS is off by default; opt in via Cors:AllowedOrigins (use "*" for any) to
  let another origin's flagd-ui use this instance as a remote.

Fixes
- Routed flag edit page: load the flag from the route key (was always empty /
  "Create"), fix its route segments, and stop MetadataEditor from crashing
  (JSON.parse(JSON.stringify(signalFn)) -> "undefined is not valid JSON").
- .dockerignore excludes local build artifacts so they don't leak into the image.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Rename the leading breadcrumb from "Breadcrumb" to "Overview".
- Make it clickable: on wide layouts it navigates to the root; on mobile it
  toggles the nav drawer (the overview lives there, so it acts like the burger).
- Link the collection-name crumb back to its flags-file detail view when on a
  sub-page (edit/settings).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants