Feature/backend ts support andunification of auth settings on frontend#654
Conversation
…boundaries The backend had started to grow beyond what the frontend-only tooling setup could support safely. We were still running server code directly from /server, linting mainly the client, and relying on path assumptions such as "../.." that only worked in the source layout. That created three problems: - backend alias imports were hard to resolve consistently in the editor, ESLint, and the runtime - server code had no enforced module boundary rules, so cross-module deep imports could bypass intended public entry points - building the backend into a separate output directory would break repo-level lookups for package.json, .env, dist, and public assets because those paths were derived from source-only relative assumptions This change makes the backend tooling explicit and runtime-safe. A dedicated backend TypeScript config now lives in server/tsconfig.json, with tsconfig.server.json reduced to a compatibility shim. This gives the language service and backend tooling a canonical project rooted in /server while still preserving top-level compatibility for any existing references. The backend alias mapping now resolves relative to /server, which avoids colliding with the frontend's "@/..." -> "src/*" mapping. The package scripts were updated so development runs through tsx with the backend tsconfig, build now produces a compiled backend in dist-server, and typecheck/lint cover both client and server. A new build-server.mjs script runs TypeScript and tsc-alias and cleans dist-server first, which prevents stale compiled files from shadowing current source files after refactors. To make the compiled backend behave the same as the source backend, runtime path resolution was centralized in server/utils/runtime-paths.js. Instead of assuming fixed relative paths from each module, server entry points now resolve the actual app root and server root at runtime. That keeps package.json, .env, dist, public, and default database paths stable whether code is executed from /server or from /dist-server/server. ESLint was expanded from a frontend-only setup into a backend-aware one. The backend now uses import resolution tied to the backend tsconfig so aliased imports resolve correctly in linting, import ordering matches the frontend style, and unused/duplicate imports are surfaced consistently. Most importantly, eslint-plugin-boundaries now enforces server module boundaries. Files under server/modules can no longer import another module's internals directly. Cross-module imports must go through that module's barrel file (index.ts/index.js). boundaries/no-unknown was also enabled so alias-resolution gaps cannot silently bypass the rule. Together, these changes make the backend buildable, keep runtime path resolution stable after compilation, align server tooling with the client where appropriate, and enforce a stricter modular architecture for server code.
📝 WalkthroughWalkthroughAdds TypeScript build infrastructure for the backend with path resolution utilities, refactors database schema into JavaScript constants, updates ESLint to enforce backend module boundaries, consolidates provider authentication state handling, unifies provider typing from Changes
Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (3)
src/components/provider-auth/types.ts (1)
22-27: Consider deriving the initial map fromCLI_PROVIDERSto reduce duplication.The provider list is duplicated in both
CLI_PROVIDERSandcreateInitialProviderAuthStatusMap. If a provider is added or removed, both places need updating.♻️ Suggested refactor to derive from CLI_PROVIDERS
-export const createInitialProviderAuthStatusMap = (loading = true): ProviderAuthStatusMap => ({ - claude: { authenticated: false, email: null, method: null, error: null, loading }, - cursor: { authenticated: false, email: null, method: null, error: null, loading }, - codex: { authenticated: false, email: null, method: null, error: null, loading }, - gemini: { authenticated: false, email: null, method: null, error: null, loading }, -}); +export const createInitialProviderAuthStatusMap = (loading = true): ProviderAuthStatusMap => ( + Object.fromEntries( + CLI_PROVIDERS.map((provider) => [ + provider, + { authenticated: false, email: null, method: null, error: null, loading }, + ]), + ) as ProviderAuthStatusMap +);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/provider-auth/types.ts` around lines 22 - 27, The createInitialProviderAuthStatusMap duplicates the provider list instead of using the canonical CLI_PROVIDERS; change createInitialProviderAuthStatusMap to build the ProviderAuthStatusMap by iterating over CLI_PROVIDERS (e.g., using reduce or Object.fromEntries) and returning the same default object shape ({ authenticated: false, email: null, method: null, error: null, loading }) for each provider key so adding/removing providers only requires updating CLI_PROVIDERS; reference createInitialProviderAuthStatusMap and CLI_PROVIDERS when making the change and preserve the return type ProviderAuthStatusMap.src/components/provider-auth/hooks/useProviderAuthStatus.ts (1)
103-108: Consider whether exposingsetProviderAuthStatusis necessary.The hook exposes
setProviderAuthStatusdirectly, but the hook already providescheckProviderAuthStatusandrefreshProviderAuthStatusesfor state updates. Direct exposure of the state setter could lead to inconsistent state if callers bypass the proper update flow (e.g., setting authenticated without clearing loading).If external state manipulation is needed, consider exposing a more controlled API like
setProviderStatus(which is already defined internally but not exported).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/provider-auth/hooks/useProviderAuthStatus.ts` around lines 103 - 108, The hook currently returns the raw state setter setProviderAuthStatus which allows callers to mutate providerAuthStatus inconsistently; instead, remove setProviderAuthStatus from the returned object and expose the controlled updater setProviderStatus (or rename to setProviderStatus if required) so callers must go through the internal logic that maintains loading/authenticated/error invariants; update the returned object in useProviderAuthStatus to include setProviderStatus (or the existing internal function) and remove setProviderAuthStatus, and adjust any call sites to use the new controlled API (checkProviderAuthStatus and refreshProviderAuthStatuses remain unchanged).src/components/sidebar/view/Sidebar.tsx (1)
237-244: Prefer guarded coercion overas LLMProviderfor search payload providers.
provideris runtime string input; casting it directly can leak unsupported values into session state.Proposed refactor
- const resolvedProvider = (provider || 'claude') as LLMProvider; + const resolvedProvider: LLMProvider = + provider === 'claude' || provider === 'cursor' || provider === 'codex' || provider === 'gemini' + ? provider + : 'claude';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/sidebar/view/Sidebar.tsx` around lines 237 - 244, The code casts the runtime string provider to LLMProvider using "as" in the onConversationResultClick handler which can leak invalid values into sessionObj.__provider; replace the unsafe cast by validating provider against the known provider set (e.g., an isValidProvider/type-guard or a lookup of allowed LLMProvider values) and only assign provider to resolvedProvider if it passes validation, otherwise fall back to the default ('claude'); update the resolvedProvider creation and ensure sessionObj.__provider uses that validated value (refer to onConversationResultClick, resolvedProvider, sessionObj, and LLMProvider).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@package.json`:
- Around line 27-33: The package.json's files whitelist is missing the runtime
artifact directory referenced by the "server" script ("node
dist-server/server/index.js"); update the package.json "files" array to include
"dist-server/" so the dist-server directory (and its server/index.js) is
packaged on publish; locate the "files" array in package.json and add the string
"dist-server/" alongside the existing entries ("server/", "shared/", "dist/",
"scripts/", "README.md").
In `@src/components/chat/hooks/useChatProviderState.ts`:
- Around line 14-16: The state initializer in useChatProviderState (the
useState<LLMProvider> call that reads localStorage.getItem('selected-provider'))
currently asserts the stored string as LLMProvider without runtime validation;
add a small validation helper (e.g., isValidProvider(value): value is
LLMProvider) that checks against the allowed provider union/list and use it in
the initializer and anywhere you read 'selected-provider' (and in setters like
setProvider) so that invalid or legacy strings fall back to 'claude' and never
populate state with an invalid provider.
In `@src/components/chat/view/ChatInterface.tsx`:
- Around line 209-212: The code casts localStorage.getItem('selected-provider')
to LLMProvider directly (providerVal) and then passes it to
sessionStore.refreshFromServer via selectedSession.__provider || providerVal,
allowing invalid runtime strings to reach the API; change this to
parse-and-validate the runtime value against the allowed LLMProvider literals
('claude','cursor','codex','gemini') before use: read the stored string, verify
it matches one of the valid provider values, otherwise fall back to a safe
default ('claude'); then pass the validated provider to refreshFromServer (use
the existing symbols providerVal, selectedSession.__provider, LLMProvider, and
sessionStore.refreshFromServer) so only validated provider values are sent to
the backend.
---
Nitpick comments:
In `@src/components/provider-auth/hooks/useProviderAuthStatus.ts`:
- Around line 103-108: The hook currently returns the raw state setter
setProviderAuthStatus which allows callers to mutate providerAuthStatus
inconsistently; instead, remove setProviderAuthStatus from the returned object
and expose the controlled updater setProviderStatus (or rename to
setProviderStatus if required) so callers must go through the internal logic
that maintains loading/authenticated/error invariants; update the returned
object in useProviderAuthStatus to include setProviderStatus (or the existing
internal function) and remove setProviderAuthStatus, and adjust any call sites
to use the new controlled API (checkProviderAuthStatus and
refreshProviderAuthStatuses remain unchanged).
In `@src/components/provider-auth/types.ts`:
- Around line 22-27: The createInitialProviderAuthStatusMap duplicates the
provider list instead of using the canonical CLI_PROVIDERS; change
createInitialProviderAuthStatusMap to build the ProviderAuthStatusMap by
iterating over CLI_PROVIDERS (e.g., using reduce or Object.fromEntries) and
returning the same default object shape ({ authenticated: false, email: null,
method: null, error: null, loading }) for each provider key so adding/removing
providers only requires updating CLI_PROVIDERS; reference
createInitialProviderAuthStatusMap and CLI_PROVIDERS when making the change and
preserve the return type ProviderAuthStatusMap.
In `@src/components/sidebar/view/Sidebar.tsx`:
- Around line 237-244: The code casts the runtime string provider to LLMProvider
using "as" in the onConversationResultClick handler which can leak invalid
values into sessionObj.__provider; replace the unsafe cast by validating
provider against the known provider set (e.g., an isValidProvider/type-guard or
a lookup of allowed LLMProvider values) and only assign provider to
resolvedProvider if it passes validation, otherwise fall back to the default
('claude'); update the resolvedProvider creation and ensure
sessionObj.__provider uses that validated value (refer to
onConversationResultClick, resolvedProvider, sessionObj, and LLMProvider).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: ebb19162-2fa2-4190-97cb-424187bf22f5
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (49)
.gitignoreeslint.config.jspackage.jsonscripts/build-server.mjsserver/cli.jsserver/database/db.jsserver/database/init.sqlserver/database/schema.jsserver/index.jsserver/load-env.jsserver/routes/commands.jsserver/tsconfig.jsonserver/utils/runtime-paths.jssrc/components/chat/hooks/useChatComposerState.tssrc/components/chat/hooks/useChatProviderState.tssrc/components/chat/hooks/useChatRealtimeHandlers.tssrc/components/chat/hooks/useChatSessionState.tssrc/components/chat/types/types.tssrc/components/chat/view/ChatInterface.tsxsrc/components/chat/view/subcomponents/ChatMessagesPane.tsxsrc/components/chat/view/subcomponents/ProviderSelectionEmptyState.tsxsrc/components/llm-logo-provider/SessionProviderLogo.tsxsrc/components/onboarding/view/Onboarding.tsxsrc/components/onboarding/view/subcomponents/AgentConnectionCard.tsxsrc/components/onboarding/view/subcomponents/AgentConnectionsStep.tsxsrc/components/onboarding/view/types.tssrc/components/onboarding/view/utils.tssrc/components/provider-auth/hooks/useProviderAuthStatus.tssrc/components/provider-auth/types.tssrc/components/provider-auth/view/ProviderLoginModal.tsxsrc/components/settings/constants/constants.tssrc/components/settings/hooks/useSettingsController.tssrc/components/settings/types/types.tssrc/components/settings/view/Settings.tsxsrc/components/settings/view/tabs/agents-settings/AgentsSettingsTab.tsxsrc/components/settings/view/tabs/agents-settings/types.tssrc/components/sidebar/hooks/useSidebarController.tssrc/components/sidebar/types/types.tssrc/components/sidebar/view/Sidebar.tsxsrc/components/sidebar/view/subcomponents/SidebarProjectItem.tsxsrc/components/sidebar/view/subcomponents/SidebarProjectList.tsxsrc/components/sidebar/view/subcomponents/SidebarProjectSessions.tsxsrc/components/sidebar/view/subcomponents/SidebarSessionItem.tsxsrc/constants/config.tssrc/stores/useSessionStore.tssrc/types/app.tstsconfig.jsontsconfig.server.jsonvite.config.js
💤 Files with no reviewable changes (4)
- src/components/settings/constants/constants.ts
- src/components/onboarding/view/types.ts
- server/database/init.sql
- src/components/onboarding/view/utils.ts
|
@blackmammoth not sure if you are going to submit this PR as is but it will be very difficult to review it as it is very big. |
…sconfig.server.json
…-auth-settings-on-frontend
…ngs-on-frontend' of https://github.com/siteboon/claudecodeui into feature/backend-ts-support-andunification-of-auth-settings-on-frontend
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@package.json`:
- Around line 38-39: Update the lint-staged configuration so staged backend
files under the new server/ directory are linted before commit: adjust the
lint-staged globs (the package.json "lint" / "lint:fix" commands reference
server/) to include server/**/* (or specific extensions like
server/**/*.{js,ts,tsx}) in addition to src/**/* so pre-commit ESLint runs the
same scope as the lint and lint:fix scripts; locate the "lint-staged" entry in
package.json and mirror the new backend path there.
- Around line 27-35: The package.json exposes runtime entrypoints that point at
source files ("main": "server/index.js" and "bin.cloudcli": "server/cli.js")
while the runtime scripts and build output use the compiled artifact under
dist-server/server/index.js; update the published entrypoints to reference the
compiled output (e.g., set "main" to "dist-server/server/index.js" and
"bin.cloudcli" to "dist-server/server/cli.js") and ensure any npm scripts that
run the package (like the "server" script) are consistent with this compiled
path so installed consumers execute the built artifact rather than source.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
Summary
This PR does two related jobs:
Although the commits span both frontend and backend concerns, they all move the codebase in the same direction: clearer ownership, less hidden coupling, and tooling that matches how the app actually runs.
Why this was needed
On the frontend, provider authentication state and settings behavior had become spread across onboarding, settings, and agent-management UI. Some of that logic still depended on project-specific context even though provider auth status is global application state, not project state. There were also stale controller arguments and naming that no longer reflected the domain well. That made the code harder to reuse and easier to break when one surface changed another.
On the backend, the project had outgrown a frontend-first tooling setup. Alias imports, linting, runtime path assumptions, and the actual server build path were not aligned. We also had no architecture guardrail around
server/modules, so module internals could be imported directly without going through a public entry point. That kind of coupling is cheap in the short term but costly as modules grow.This PR addresses both categories by reducing implicit coupling and making the intended boundaries explicit.
Frontend changes
Decoupled provider auth state from project-specific settings flows
The provider auth status logic was moved into a dedicated custom hook. This was needed because onboarding, settings, and related UI were each carrying pieces of the same provider-status behavior. Centralizing that logic gives the app a single place to answer questions such as which providers are authenticated, how loading/error state should behave, and which UI branches should render.
This reduces duplication and makes the provider-auth behavior reusable across multiple screens without forcing those screens to know how the status is derived.
Removed project dependency from settings/onboarding logic
Settings and onboarding were carrying project-dependent behavior that did not belong in those flows. That dependency made global settings behavior harder to reuse and harder to test because screens that should only care about provider/account state were indirectly coupled to project context.
This was simplified so settings/onboarding can make decisions from the right source of truth instead of depending on project state they do not actually own.
Removed obsolete controller surface
useSettingsControllerno longer takes the unusedonCloseargument. That argument had become stale and keeping it around made the API noisier and more misleading than the actual behavior.Removing it makes the controller contract smaller and clearer.
Renamed
SessionProvidertoLLMProviderThe previous name suggested session ownership, but the actual concept being modeled is the selected large-language-model provider. Renaming this to
LLMProvidermakes the code read closer to the product/domain language and removes a layer of translation when navigating chat, onboarding, sidebar, and provider-auth code.This kind of rename matters because it reduces ambiguity across components, hooks, types, and store state.
Added frontend
@alias supportThe frontend now explicitly supports
@alias imports through the client TypeScript/Vite configuration. This was needed to make imports more consistent and less brittle as the UI surface grew.That also sets the stage for having symmetrical but separate alias behavior between frontend and backend instead of forcing both sides through one shared resolution model.
Tailwind ordering cleanup
A small Tailwind class order cleanup was included to keep formatting consistent with the lint/style direction of the rest of the PR.
Backend changes
Replaced raw SQL bootstrap file with schema-owned SQL
Database schema creation moved away from the standalone
init.sqlflow and intoschema.js, with database initialization updated to consume that shared schema source. This was needed because schema definitions and initialization behavior were drifting apart.By keeping table creation SQL in one JS-owned schema module, database setup becomes easier to evolve, easier to review, and less likely to fall out of sync between initialization and runtime usage.
Added a real backend TypeScript project and build pipeline
The backend now has a canonical TypeScript config in
server/tsconfig.json, whiletsconfig.server.jsonremains as a compatibility shim. Development runs throughtsx, the server builds intodist-server, and the backend build is handled by a dedicated script that compiles and rewrites aliases.This was needed because the backend was no longer just a set of source files that happened to run. It needed a real project boundary for the editor, typechecker, linter, and build system to agree on.
The new setup makes backend tooling explicit instead of incidental.
Fixed backend alias resolution across TypeScript and ESLint
Backend alias resolution now points to the backend-specific tsconfig instead of piggybacking on the frontend config model. This was necessary because the frontend and backend both use
@, but they do not mean the same thing. The frontend maps@tosrc, while the backend needs@rooted atserver.Without separating those concerns, one side of the stack will always be “correct” only by accident.
The new configuration makes alias imports resolve consistently in the editor, in type checking, and in lint rules.
Made runtime path handling stable after compilation
Several server entry points were relying on source-layout-relative assumptions like walking
../..from the current file. That works only while code runs directly fromserver. Once the backend is compiled intodist-server/server, those assumptions become fragile and can break access to top-level resources such aspackage.json,.env,public,dist, and default database paths.This PR introduces shared runtime path helpers so backend code resolves the real app root intentionally instead of guessing it from local file layout. That keeps runtime behavior consistent in both source and built forms.
Enforced server module boundaries through barrel files
eslint-plugin-boundariesnow treats each folder underserver/modulesas an explicit module boundary. Cross-module imports are only allowed through the target module’s barrel file (index.ts/index.js), while deep imports into another module’s internals are blocked.This was needed because once modules exist, the lack of import boundaries quickly turns them into folders rather than real modules. Requiring barrel imports creates a clear public API for each module and prevents accidental coupling to private implementation files.
The configuration also now fails unknown dependency classifications so alias-resolution gaps cannot silently bypass the boundary rule.
Brought backend import hygiene in line with the frontend
Backend ESLint now enforces the same import ordering behavior as the frontend, including blank lines between groups, along with duplicate/unresolved import checks and unused import reporting.
This improves consistency across the codebase and makes backend files follow the same readability and hygiene expectations as frontend files.
How the pieces fit together
The frontend auth/settings changes and the backend tooling changes solve different immediate problems, but they follow the same design principle:
On the frontend, that means provider auth status lives behind a reusable hook, settings controllers expose only the arguments they actually need, and provider naming matches the real concept.
On the backend, that means aliases resolve through the correct project config, build output is treated as a real runtime target, runtime paths are resolved deliberately, and server modules expose stable public entry points instead of encouraging deep imports.
Validation
This PR was validated through:
Result
After this PR:
dist-serversafelySummary by CodeRabbit
New Features
Infrastructure