chore: clear type-cast backlog and promote to hard gate#2337
Conversation
…e-positive fixes - Exclude test files (/__tests__/, /test/) from cast scanning - Extend safe-cast annotation lookback through consecutive comment lines (was 1 line; now scans the entire comment block preceding the cast) - Add NATURAL_LANGUAGE_CAST pattern to skip content-string false positives - Add ArrayBuffer, ReadableStream, SQL to SAFE_CAST_PATTERNS
…ditions - computePackWeights: remove redundant WeightUnit cast - PackDetailScreen/PackCard: annotation for Pack union narrowing - usePackTemplateSummary: remove redundant casts - TrailConditionReportCard: remove redundant casts - treaty hooks: annotate with safe-cast where response shape matches schema
…ps, components) - treaty response hooks: safe-cast annotations for shape-match casts - ToolInvocationRenderer: inline safe-cast on each discriminated case branch - Icon components: safe-cast for ComponentProps/SfSymbolName icon type coercions - store.ts obs(): safe-cast for Legend-State Proxy access pattern
- guides generate page: type ContentCategory at Object.entries; DifficultyLevel uses assertEnum - guides sync-to-r2: inline safe-cast for SyncEnv → ValidatedEnv subset - landing icons: safe-cast for Lucide icon type narrowing
…, config, web-ui) - env-validation: safe-cast annotations for Cloudflare Worker runtime bindings - api middleware/services: safe-cast for treaty/generic boundary casts - analytics: safe-cast for DuckDB row result types - guards narrow.ts: assertEnum helper for enum type narrowing - cli, config, web-ui: safe-cast for legitimate type boundary casts
Type-cast backlog cleared (130 → 0). Remove continue-on-error from CI and add bun check:casts:strict to the pre-push clean-checks gate.
…acros Adds scripts/lint/no-unauth-routes.ts which scans packages/api/src/routes for Elysia route handlers (.get/.post/.put/.patch/.delete/.all) that lack isAuthenticated/isAdmin/isValidApiKey and have no // public-route: annotation. - Excludes the admin/ subtree (uses Cloudflare Access + Basic/JWT auth via onBeforeHandle, not Elysia macros) - Annotates the 10 intentionally-public auth routes (login, register, verify-email, resend-verification, forgot-password, reset-password, refresh, logout, apple, google) with descriptive // public-route: reasons - Wired into check-all.ts, lint:custom, lefthook.yml pre-push gate, and CI
- no-duplicate-guards.ts: use template literal for path separator (useTemplate) - no-unauth-routes.ts: restructure while loop to for(;;) to avoid noAssignInExpressions
Treaty response type has createdAt as string | undefined but the PackItem schema type requires string; the cast bridges this gap.
The /alltrails/preview endpoint is a link-preview proxy that fetches OG metadata from AllTrails. No user data is accessed or modified, so it intentionally omits auth macros.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ 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 |
Coverage Report for API Unit Tests Coverage (./packages/api)
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Coverage Report for Expo Unit Tests Coverage (./apps/expo)
File Coverage
|
||||||||||||||||||||||||||||||||||||||
🔒 add no-unauth-routes lint check
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Promotes strict type-cast checking to a hard gate and clears the backlog by annotating intentional casts, while adding a new lint rule to detect unauthenticated Elysia routes.
Changes:
- Harden
check-type-casts(--strict) in CI and pre-push, and annotate intentional casts with// safe-cast:. - Add a new custom lint rule (
no-unauth-routes) and integrate it intocheck:all,lint:custom, CI, and lefthook. - Fix a Biome
useTemplatelint issue and remove a few unnecessary casts now that types align.
Reviewed changes
Copilot reviewed 76 out of 76 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| scripts/lint/no-unauth-routes.ts | New lint script to flag Elysia routes missing auth macros or // public-route: annotation |
| scripts/lint/no-duplicate-guards.ts | Biome useTemplate fix in excluded-path check |
| scripts/check-all.ts | Runs the new unauth-route check as part of full check suite |
| packages/web-ui/src/components/form.tsx | Adds // safe-cast: annotation for React context sentinel casts |
| packages/guards/src/narrow.ts | Annotates cast used for Object.entries iteration after object narrowing |
| packages/config/src/config.ts | Annotates cast used for Object.values iteration after isObject guard |
| packages/cli/src/shared.ts | Annotates casts when printing tabular DB/DuckDB-like result objects |
| packages/checks/src/check-type-casts.ts | Tightens strictness behavior (annotations, exclusions, safe patterns) |
| packages/api/src/utils/env-validation.ts | Annotates Worker binding casts and global env priming casts |
| packages/api/src/utils/csv-utils.ts | Annotates generic boundary cast for JSON parse |
| packages/api/src/utils/compute-pack.ts | Removes WeightUnit cast (relies on updated typing) |
| packages/api/src/utils/auth.ts | Annotates JWT payload cast as intentional boundary |
| packages/api/src/services/refreshTokenService.ts | Annotates Drizzle SQL casts where combinators return `SQL |
| packages/api/src/services/r2-bucket.ts | Annotates stream/ArrayBuffer/generic JSON boundary casts |
| packages/api/src/services/etl/processValidItemsBatch.ts | Annotates cast after upstream validation guarantees |
| packages/api/src/routes/weather.ts | Annotates typed casts on WeatherAPI JSON responses |
| packages/api/src/routes/packs/index.ts | Annotates NewPackItem cast used due to inference mismatch |
| packages/api/src/routes/auth/index.ts | Adds // public-route: annotations to auth endpoints |
| packages/api/src/routes/alltrails.ts | Adds // public-route: annotation for preview proxy endpoint |
| packages/api/src/middleware/auth.ts | Annotates AuthUser casts after JWT validation |
| packages/api/src/index.ts | Annotates Worker entrypoint casts (env + app.fetch + queue batch narrowing) |
| packages/api/src/containers/AppContainer.ts | Annotates module-level Workers env cast to typed Env |
| packages/api-client/src/index.ts | Annotates error-field access cast and generic boundary cast on response |
| packages/analytics/src/core/spec-parser.ts | Annotates DuckDB row-to-object casts |
| packages/analytics/src/core/local-cache.ts | Annotates generic boundary cast for typed row objects |
| packages/analytics/src/core/entity-resolver.ts | Annotates DuckDB row-to-object cast |
| packages/analytics/src/core/enrichment.ts | Annotates DuckDB row-to-object casts |
| package.json | Adds unauth-route lint script to lint:custom |
| lefthook.yml | Adds unauth-route lint + strict cast check to pre-push gate |
| apps/landing/lib/icons.tsx | Annotates dynamic Lucide icon lookup casts |
| apps/guides/scripts/sync-to-r2.ts | Annotates env-cast used to satisfy R2BucketService runtime needs |
| apps/guides/components/search.tsx | Annotates DOM event.target as Node cast |
| apps/guides/app/dev/generate/page.tsx | Replaces enum casts with assertEnum and tightens Object.entries typing |
| apps/expo/lib/store.ts | Annotates cast bridging Legend-State Proxy typing mismatch |
| apps/expo/features/wildlife/hooks/useWildlifeIdentification.ts | Annotates cast after response guard validates array |
| apps/expo/features/weather/screens/LocationPreviewScreen.tsx | Annotates cast for formatted weather data |
| apps/expo/features/weather/hooks/useWeatherAlert.ts | Annotates cast for subset type used by alert generator |
| apps/expo/features/weather/hooks/useLocationSearch.ts | Annotates cast for formatted weather location |
| apps/expo/features/weather/hooks/useLocationRefresh.ts | Annotates casts for partial weather location updates |
| apps/expo/features/trips/screens/TripDetailScreen.tsx | Annotates cast used across store hydration/guard boundary |
| apps/expo/features/trips/components/TripForm.tsx | Annotates cast for useForm defaultValues inference |
| apps/expo/features/trail-conditions/hooks/useTrailConditionReports.ts | Annotates JSON parse and Treaty response casts |
| apps/expo/features/trail-conditions/components/TrailConditionReportCard.tsx | Removes enum casts where indexing accepts typed keys |
| apps/expo/features/packs/utils/computePackWeights.ts | Removes WeightUnit cast (relies on updated typing) |
| apps/expo/features/packs/store/packs.ts | Annotates Zod-validated response casts to store types |
| apps/expo/features/packs/store/packItems.ts | Annotates Zod-validated response casts and adds parentheses for clarity |
| apps/expo/features/packs/screens/PackDetailScreen.tsx | Replaces narrative cast comment with // safe-cast: annotation |
| apps/expo/features/packs/screens/ItemsScanScreen.tsx | Annotates cast for untyped router params into SelectedImage |
| apps/expo/features/packs/hooks/useSeasonSuggestions.ts | Annotates Treaty response cast |
| apps/expo/features/packs/hooks/usePackItemDetailsFromApi.ts | Annotates Zod parse + cast divergence |
| apps/expo/features/packs/hooks/usePackGapAnalysis.ts | Annotates Treaty response cast |
| apps/expo/features/packs/hooks/useImageDetection.ts | Annotates Treaty response cast |
| apps/expo/features/packs/hooks/useDuplicatePack.ts | Annotates Treaty response cast |
| apps/expo/features/packs/components/SimilarItemsForPackItem.tsx | Annotates cast for untyped items field |
| apps/expo/features/packs/components/PackCard.tsx | Replaces narrative cast comment with // safe-cast: annotation |
| apps/expo/features/pack-templates/store/packTemplates.ts | Annotates Zod-validated response casts to store types |
| apps/expo/features/pack-templates/store/packTemplateItems.ts | Annotates Zod-validated casts and adds parentheses for clarity |
| apps/expo/features/pack-templates/screens/ItemsScanScreen.tsx | Annotates cast for untyped router params into SelectedImage |
| apps/expo/features/pack-templates/hooks/usePackTemplateSummary.ts | Removes WeightUnit casts after typing alignment |
| apps/expo/features/pack-templates/hooks/useGenerateTemplateFromOnlineContent.ts | Annotates Treaty error/value probing and response cast |
| apps/expo/features/catalog/screens/CatalogItemsScreen.tsx | Annotates Treaty response casts and refactors pagination mapping |
| apps/expo/features/catalog/hooks/useSimilarItems.ts | Annotates Treaty response cast |
| apps/expo/features/catalog/components/SimilarItems.tsx | Removes unnecessary cast where data is already typed |
| apps/expo/features/catalog/components/CatalogBrowserModal.tsx | Annotates Treaty response casts for popular/search/paginated items |
| apps/expo/features/auth/hooks/useAuthActions.ts | Annotates Href string cast fallback + user cast to local type |
| apps/expo/features/ai/hooks/useReportedContent.ts | Annotates Treaty response cast |
| apps/expo/features/ai/hooks/useReportContent.ts | Annotates Treaty response cast |
| apps/expo/features/ai/components/ToolInvocationRenderer.tsx | Annotates discriminated-union casts per tool type |
| apps/expo/features/ai/components/ChatBubble.tsx | Annotates cast after tool- prefix check |
| apps/expo/features/ai-packs/hooks/useGeneratedPacks.ts | Annotates Treaty response cast |
| apps/expo/components/Icon/get-icon-names.ts | Annotates fallback/icon-name union casts |
| apps/expo/components/Icon/Icon.tsx | Annotates icon-name union casts for runtime fallback chain |
| apps/expo/components/Icon/Icon.ios.tsx | Annotates icon-name union casts for runtime fallback chain |
| apps/expo/app/(app)/current-pack/[id].tsx | Restores cast with // safe-cast: for Treaty vs schema createdAt mismatch |
| apps/admin/lib/api.ts | Annotates generic boundary cast for res.json() |
| .github/workflows/checks.yml | Adds unauth-route check step and makes strict cast check a hard gate |
Comments suppressed due to low confidence (8)
scripts/lint/no-unauth-routes.ts:1
- Excluding an
admindirectory from the unauth-route audit creates a blind spot that can undermine the purpose of this hard gate (admin routes are often the most sensitive). Ifpackages/api/src/routes/adminexists and should be checked, remove'admin'fromEXCLUDED_DIRS; if it must be excluded, add a clear comment explaining why and consider replacing this with a more explicit allowlist/denylist scoped to the actual route tree structure.
scripts/lint/no-unauth-routes.ts:1 - Scanning the entire route-call
spanforisAuthenticated/isAdmin/isValidApiKeycan yield false negatives if those tokens appear inside the handler body (e.g., in an object literal, comment, or string) rather than in the route options. Since this script is now a CI gate, it should avoid missing public routes; consider parsing just the options argument (commonly the last argument) and only searching within that object literal (ideally via a TypeScript AST parse rather than regex on raw text).
scripts/lint/no-unauth-routes.ts:1 - Scanning the entire route-call
spanforisAuthenticated/isAdmin/isValidApiKeycan yield false negatives if those tokens appear inside the handler body (e.g., in an object literal, comment, or string) rather than in the route options. Since this script is now a CI gate, it should avoid missing public routes; consider parsing just the options argument (commonly the last argument) and only searching within that object literal (ideally via a TypeScript AST parse rather than regex on raw text).
scripts/lint/no-unauth-routes.ts:1 - The parenthesis-walk is a text-level approximation and does not account for parentheses inside strings/template literals/regex literals, which can cause incorrect
callEnddetection and therefore missed/incorrect violations. Given this is a security-oriented lint, consider switching to a TypeScript parser (e.g., TypeScript compiler API / ts-morph) to reliably locate CallExpressions and their argument lists; this will also make it much easier to isolate the options object and reduce false positives/negatives.
scripts/lint/no-unauth-routes.ts:1 - Computing
callLineby slicing from the start of the file and splitting on every match makes this O(n²) in the number of routes in a file (each match re-scans earlier content). Since this runs in CI/pre-push, consider precomputing newline offsets once (or tracking line number incrementally while scanning) to keep route-heavy files fast.
scripts/lint/no-unauth-routes.ts:1 - Deriving
relPathvia string replacement assumes POSIX separators and can produce incorrect output on Windows (or if paths are normalized differently). Preferpath.relative(ROOT, filePath)(and optionally normalize separators for display) to keep this script portable and avoid confusing file paths in CI output.
packages/checks/src/check-type-casts.ts:1 - These exclusion patterns hard-code POSIX path separators. If this script is ever run on Windows (local dev or CI),
\\__tests__\\and\\test\\directories won’t match and could unexpectedly start failing strict cast checks. Consider normalizing file paths to POSIX separators before regex tests, or use a separator-agnostic regex (e.g., matching both/and\\).
packages/checks/src/check-type-casts.ts:1 - These exclusion patterns hard-code POSIX path separators. If this script is ever run on Windows (local dev or CI),
\\__tests__\\and\\test\\directories won’t match and could unexpectedly start failing strict cast checks. Consider normalizing file paths to POSIX separators before regex tests, or use a separator-agnostic regex (e.g., matching both/and\\).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - name: Check for unauthenticated routes | ||
| run: bun scripts/lint/no-unauth-routes.ts |
There was a problem hiding this comment.
bun lint:custom now includes bun run scripts/lint/no-unauth-routes.ts (per package.json), so this workflow runs the unauth-route check twice. To keep CI faster and reduce noisy duplication, remove the standalone step or remove no-unauth-routes from lint:custom and keep it as a dedicated CI step (one or the other).
| - name: Check for unauthenticated routes | |
| run: bun scripts/lint/no-unauth-routes.ts |
chore: clear type-cast backlog and promote to hard gate
Replaces #2317 (auto-closed when base branch merged).
Summary
check-type-castsfrom advisory to a hard CI gate (--strictflag)check-type-casts --strictto lefthook pre-push hookas unknown as Tcasts introduced by the Zod/Treaty migration across expo, packages, and appscurrent-pack/[id].tsxwith// safe-cast:annotation (Treaty responsecreatedAt?: stringvs Zod schemastring)useTemplaterule inno-duplicate-guards.tsTest plan
bun run check:casts:strictpasses (no unsafe casts)check-type-casts --strictas unknown ascasts have// safe-cast:annotation on the preceding line🤖 Generated with Claude Code