Auto-generate TypeScript route definitions from endpoint metadata#87
Merged
antosubash merged 4 commits intomainfrom Apr 6, 2026
Merged
Auto-generate TypeScript route definitions from endpoint metadata#87antosubash merged 4 commits intomainfrom
antosubash merged 4 commits intomainfrom
Conversation
- Add RouteTemplate/HttpMethod fields to EndpointInfoRecord and ViewInfoRecord - Read `public const string Route` from endpoint classes in SymbolDiscovery - Create RoutesEmitter generating C# `ModuleRoutes` static class with typed helpers - Create TypeScriptRoutesEmitter generating TS `routes` const object - Add SM0054 diagnostic (info) for endpoints missing Route const - Add tools/extract-routes.mjs and MSBuild ExtractRoutes target - Move route constants to Contracts projects with Routes nested class - Add Route/Method const fields to all 185 endpoint classes across 16 modules - Update Map() calls to reference Route constants instead of string literals
- Make HasAnyRouteTemplates internal static on RoutesEmitter, remove
duplicate from TypeScriptRoutesEmitter
- Remove dead moduleIndex variable from TypeScriptRoutesEmitter
- Remove unused comma parameter from EmitTsRouteEntry
- Merge two ReadRouteConstFields overloads into one returning a tuple
- Fix regex to handle ASP.NET catch-all params ({**key}) properly
- Apply StripRouteConstraints to static routes too (fixes {**key} leak)
- Simplify BuildInterpolatedRoute to delegate to StripRouteConstraints
- Remove misleading comment
- Use direct file access in extract-routes.mjs instead of readdirSync
C# side: Replace TypedResults.Redirect() hardcoded strings with ViewPrefix + Routes.* constants in Products, Orders, and Email modules. TypeScript side: Import routes from @simplemodule/client/routes and replace hardcoded router.get/post/delete and href strings in Products, Orders, Tenants, Email, Marketplace, FeatureFlags, and FileStorage. Infrastructure: Move generated routes.ts to @simplemodule/client package so all modules can import it. Update extract-routes.mjs and MSBuild targets to write to the new location.
Deploying simplemodule-website with
|
| Latest commit: |
ea92d67
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://65180722.simplemodule-website.pages.dev |
| Branch Preview URL: | https://claude-type-safe-routes-jelu.simplemodule-website.pages.dev |
antosubash
added a commit
that referenced
this pull request
Apr 6, 2026
Auto-merge of PR #87 (type-safe routes) placed the SM0054 endpoint/view Route-const diagnostics outside the 'foreach (var module in data.Modules)' loop, leaving 'module' out of scope. Move them back inside the loop.
antosubash
added a commit
that referenced
this pull request
Apr 6, 2026
* Fix sm CLI dev workflow and generator diagnostics for user projects - sm new project: scaffold vite.dev.config.ts so sm dev works out of the box - sm dev: fix vite invocation (vite dev → vite) and add --configLoader runner - sm doctor: fix PagesKeyPattern regex to match single-quoted Pages/index.ts keys - moduleHmrPlugin: fall back to src/modules/ path for scaffolded user projects - SM0052/SM0053 diagnostics: scope to SimpleModule.* host projects only so user projects with their own naming conventions are not falsely flagged - AgentExtensionsEmitter: remove early return that broke empty projects - SimpleModuleHostExtensions: register IHttpContextAccessor for EntityInterceptor - Directory.Build.props: guard PackageReadmeFile with Exists() to fix pack * Add passkey login design spec * Update passkey spec: fix API names, schema override, challenge storage * Add passkey login implementation plan * feat: add Identity Schema V3 and IdentityPasskeyOptions for passkey support - Configure IdentityOptions.Stores.SchemaVersion = Version3 in UsersModule to opt into the AspNetUserPasskeys table (WebAuthn passkey support) - Configure IdentityPasskeyOptions from appsettings "Passkeys" section in UsersModule - Add Passkeys config sections to appsettings.json and appsettings.Development.json - Fix HostDbContextFactory to expose a minimal service provider with IdentityOptions via UseApplicationServiceProvider so EF design-time model creation picks up SchemaVersion - Add UseApplicationServiceProvider to AddModuleDbContext for runtime parity - Add migration AddPasskeySupport creating Users_AspNetUserPasskeys table - Add HostDbContextPasskeys.cs as a seam partial for future passkey customizations * fix: use SchemaVersion override for passkey schema, revert broad DI changes Add `protected override Version SchemaVersion => IdentitySchemaVersions.Version3` to the HostDbContext partial — this compiles cleanly against Identity 10.0.3 and is the correct way to opt into the AspNetUserPasskeys table. Because SchemaVersion is now a compile-time constant on the class, the two IOptions workarounds are no longer needed: remove UseApplicationServiceProvider(sp) from ModuleDbContextOptionsBuilder (which would have applied the full app DI to every module DbContext) and revert the ServiceCollection + BuildServiceProvider block from HostDbContextFactory (design-time). * feat: add PasskeyRegisterBeginEndpoint Adds the POST /api/passkeys/register/begin endpoint that calls MakePasskeyCreationOptionsAsync and returns WebAuthn creation options JSON. Also fixes test infrastructure: UsersDbContext now overrides SchemaVersion to Version3 so the EF model includes the AspNetUserPasskeys table, and the shared test factory now calls UseApplicationServiceProvider so IdentityOptions (including SchemaVersion) are accessible during model creation. Adds appsettings.Testing.json to configure ServerDomain = "localhost" for tests. * feat: add PasskeyRegisterCompleteEndpoint Adds the POST /api/passkeys/register/complete endpoint that calls PerformPasskeyAttestationAsync to validate WebAuthn attestation and stores the passkey. Catches InvalidOperationException (no challenge cookie) and PasskeyException (crypto failure) to return 400 instead of 500. Also adds PasskeyApiEndpointTests integration test class covering all four scenarios: RegisterBegin authenticated/unauthenticated and RegisterComplete unauthenticated/invalid-credential. Tests seed a real user (passkey-test-user-id) with CreateAuthenticatedClient targeting that user ID. * fix: dispose StreamReader and verify PasskeyException handling in RegisterCompleteEndpoint Replace undisposed StreamReader with a using block (leaveOpen: true) to avoid closing the framework-owned request body stream. PasskeyException confirmed present in .NET 10 Identity — catch block retained as-is. * feat: add passkey login and management API endpoints (Tasks 4-7) Add PasskeyLoginBeginEndpoint, PasskeyLoginCompleteEndpoint, GetPasskeysEndpoint, and DeletePasskeyEndpoint with full integration test coverage (38 tests passing). * feat: add ManagePasskeysEndpoint (Inertia view) * feat: add WebAuthn browser API utility (passkey.ts) * feat: add ManagePasskeys React page * feat: add passkey sign-in button to login page * feat: pass passkeyEnabled prop from IdentityPasskeyOptions to login page * feat: register ManagePasskeys page and add Passkeys nav item to account sidebar * feat: add device type hint to passkeys list Adds transports field to the passkeys DTO and derives a human-readable device type hint (e.g. 'Built-in sensor', 'Security key (USB)') shown below the passkey name in the Manage Passkeys page. * refactor: fix passkeyEnabled in login error path, extract ToBase64Url helper * style: exclude test-projects from biome root config scan The test-projects directory contains its own biome.json which conflicts with the root configuration. Since test-projects is already excluded from the files.includes scope, explicitly adding it as an ignore pattern prevents the nested root config error. * test: add e2e tests for passkey registration, sign-in, and management * fix: make passkey e2e tests reliable across parallel runs and page navigations - serial mode + beforeEach cleanup eliminates shared-state races between tests - migrate credential ID via CDP WebAuthn.getCredentials after registration so the sign-in assertion works whether or not the credential was stored as a resident key - register route handlers before navigation so no request can slip through unintercepted - replace page.waitForTimeout(500) with try/catch on waitForURL for deterministic error reporting - fix auth.setup.ts to navigate directly to /Identity/Account/Login instead of clicking the missing "Log in" link on the landing page * fix: restore SM0054 loop inside foreach module after merge with main Auto-merge of PR #87 (type-safe routes) placed the SM0054 endpoint/view Route-const diagnostics outside the 'foreach (var module in data.Modules)' loop, leaving 'module' out of scope. Move them back inside the loop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR implements automatic generation of TypeScript route definitions from C# endpoint metadata, eliminating the need to manually maintain route constants across the codebase. Routes are now centrally defined in module Constants files and automatically exported to TypeScript.
Key Changes
New Route Generation System
RoutesEmitter.csto generate C#ModuleRoutesclass with strongly-typed route constantsTypeScriptRoutesEmitter.csto generate TypeScriptroutesobject with camelCase module names and typed route functionsextract-routes.mjstool to extract TypeScript routes from generated C# files into the client packageModule Constants Consolidation
*Constants.csfiles in.Contractsprojects for all modules (Users, Orders, Email, FileStorage, PageBuilder, Settings, Tenants, BackgroundJobs, AuditLogs, Products, RateLimiting, Marketplace, Admin, Dashboard, FeatureFlags, OpenIddict)ModuleName,RoutePrefix,ViewPrefix, and nestedRoutesclass with endpoint route constantsEndpoint Route Metadata
SymbolDiscovery.csto captureRouteTemplatefrom endpoint definitionsDiscoveryData.csto include route template information inEndpointInfoRecordSM0054to warn when endpoints are missing Route const fieldsTypeScript Integration
routes.tsin@simplemodule/clientpackage with fully typed route accessorsapiandviewssub-objectsroutes.tsre-export in template project for convenienceBuild Integration
SimpleModule.Hosting.targetsto run route extraction during buildModuleDiscovererGenerator.csto invoke new emitterspackage.jsonbuild scripts to include route extractionImplementation Details
{id},{name}) and converted to TypeScript function parametersbackgroundJobs,featureFlags) while maintaining PascalCase in C#as constfor view routes to maintain literal typeshttps://claude.ai/code/session_01WbxtDa6ZNhLvmyoANe6yi6