Conversation
WalkthroughProject-wide import reordering and deduplication aligned with new eslint-plugin-import rules. Introduces dotenv/config at top-level in API and Gateway entry points; adjusts some relative import paths. One UI component (OrganizationSwitcher) adds two callback props. No functional logic changes elsewhere; tests updated for env preload. Minor .npmrc and cache import tweaks. Changes
Sequence Diagram(s)Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
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 |
6f2ad17 to
d63b22c
Compare
Add `eslint-import-resolver-typescript` and `eslint-plugin-import` to enhance TypeScript import management. Configure rules for import order, path handling, and resolver settings in `eslint.config.mjs`.
d63b22c to
dc5b784
Compare
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
apps/api/src/routes/user.ts (1)
115-118: Critical: passkey deletion uses JS && instead of SQL and() — can delete all user passkeysUsing
&&short-circuits and drops theidpredicate, risking broader deletes. Use Drizzle'sand()and import it.-import { db, eq, tables } from "@llmgateway/db"; +import { db, eq, tables, and } from "@llmgateway/db"; - await db - .delete(tables.passkey) - .where(eq(tables.passkey.id, id) && eq(tables.passkey.userId, authUser.id)); + await db + .delete(tables.passkey) + .where(and(eq(tables.passkey.id, id), eq(tables.passkey.userId, authUser.id)));If
andisn’t re-exported from@llmgateway/db, import it fromdrizzle-orminstead.Also applies to: 5-6
apps/ui/src/components/landing/cta.tsx (1)
33-35: Add rel="noopener noreferrer" to external link opened with target="_blank".Prevents reverse‑tabnabbing and removes window.opener access.
Apply this diff:
- <a href={config.githubUrl ?? ""} target="_blank"> + <a href={config.githubUrl ?? ""} target="_blank" rel="noopener noreferrer">apps/api/src/stripe.ts (2)
179-181: Don’t log full Stripe event payloads (PII/secret leakage risk).Log only minimal metadata (id, type) or gate behind a debug flag.
Apply this diff:
- logger.info(JSON.stringify({ kind: "stripe-event", payload: event })); + logger.info({ kind: "stripe-event", id: event.id, type: event.type }); + // Optionally behind a flag: + // if (process.env.DEBUG_STRIPE === "true") logger.debug({ event });
675-679: Use subscription.current_period_end, not item-level fields.
current_period_endis on the Subscription, not on items; current code may leave planExpiresAt undefined.Apply this diff:
- const currentPeriodEnd = - subscription.items.data.length > 0 - ? subscription.items.data[0].current_period_end - : undefined; + const currentPeriodEnd = subscription.current_period_end;apps/gateway/src/lib/costs.ts (2)
61-66: Bug: falsy checks treat 0 tokens as “missing”.Use nullish checks, not truthiness.
- if ((!promptTokens || !completionTokens) && fullOutput) { + if ((promptTokens == null || completionTokens == null) && fullOutput) { @@ - if (!promptTokens && fullOutput) { + if (promptTokens == null && fullOutput) {
99-112: Bug: early return when tokens are 0.Allow zero values.
- if (!calculatedPromptTokens || !calculatedCompletionTokens) { + if (calculatedPromptTokens == null || calculatedCompletionTokens == null) {
🧹 Nitpick comments (27)
apps/ui/src/components/usage/cost-breakdown-chart.tsx (2)
139-141: Use stable keys for Cells instead of array indexIndex keys can cause unnecessary remounts when data order changes. Prefer a deterministic key like provider name.
- {chartData.map((entry, index) => ( - <Cell key={`cell-${index}`} fill={entry.color} /> - ))} + {chartData.map((entry) => ( + <Cell key={`cell-${entry.name}`} fill={entry.color} /> + ))}
144-145: Prefer locale-aware currency formattingUsing
Intl.NumberFormatimproves readability and i18n, and avoids manual string building.+ const currency = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 4 }); ... - <Tooltip - formatter={(value) => [`$${Number(value).toFixed(4)}`, "Cost"]} - /> + <Tooltip formatter={(value) => [currency.format(Number(value)), "Cost"]} /> ... - <span className="font-medium">${totalCost.toFixed(4)}</span> + <span className="font-medium">{currency.format(totalCost)}</span>Also applies to: 152-152
apps/ui/src/components/dashboard/activity-chart.tsx (3)
6-15: Co-locate the Recharts type import with the existing Recharts import (import/order)Most import-order configs group all imports from the same package together. Merge the type into the existing Recharts import to prevent lint churn.
Apply:
-import { - Bar, - BarChart, - CartesianGrid, - Legend, - ResponsiveContainer, - Tooltip, - XAxis, - YAxis, -} from "recharts"; +import { + Bar, + BarChart, + CartesianGrid, + Legend, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, + type TooltipProps, +} from "recharts"; - -import type { TooltipProps } from "recharts";Also applies to: 35-35
77-95: Avoid redeclaring TooltipProps fields; rely on Recharts’ typesYou extend TooltipProps and then re-declare active/payload/label with a custom TooltipPayload. That risks incompatibility with Recharts’ own TooltipPayload and adds maintenance overhead. Prefer composing TooltipProps and adding only your extra field.
Apply:
-interface TooltipPayload { - dataKey: string; - name: string; - value: number; - color: string; - payload: { - requestCount: number; - totalTokens: number; - cost: number; - modelBreakdown: ActivityModelUsage[]; - }; -} - -interface CustomTooltipProps extends TooltipProps<number, string> { - active?: boolean; - payload?: TooltipPayload[]; - label?: string; - breakdownField?: "requests" | "cost" | "tokens"; -} +type CustomTooltipProps = TooltipProps<number, string> & { + breakdownField?: "requests" | "cost" | "tokens"; +};
346-346: Minor: avoid repeated getUniqueModels computation in renderCompute once to reduce work during render and simplify the JSX.
Apply:
const chartData = dateRange.map((date) => { ... }); +const uniqueModels = getUniqueModels(data.activity); + return ( <Card> ... - {getUniqueModels(data.activity).length > 0 ? ( - getUniqueModels(data.activity).map((model, index) => ( + {uniqueModels.length > 0 ? ( + uniqueModels.map((model, index) => ( <Bar key={`${model}-${index}`} dataKey={model} name={model} stackId="models" fill={getModelColor(model, index)} radius={ - index === getUniqueModels(data.activity).length - 1 + index === uniqueModels.length - 1 ? [4, 4, 0, 0] : [0, 0, 0, 0] } /> ))Also applies to: 411-425
apps/ui/src/components/playground/sidebar.tsx (1)
48-49: Prefer alias import for consistency with repo-wide path conventionsSwitch to "@/components/mode-toggle" to match other UI imports and new import/resolver rules.
-import { ModeToggle } from "../mode-toggle"; +import { ModeToggle } from "@/components/mode-toggle";apps/ui/src/components/compare/hero-compare.tsx (2)
69-71: Fix typo: “Docomentation” → “Documentation”.Minor copy polish.
Apply this diff:
- text: "View Docomentation", + text: "View Documentation",
168-169: Invalid Tailwind class “h-10.5”.Tailwind doesn’t support decimal sizes without arbitrary values; this won’t compile.
Use a supported size or an arbitrary value:
- className="h-10.5 rounded-xl px-5" + className="h-10 rounded-xl px-5" + // or: className="h-[42px] rounded-xl px-5"apps/api/src/routes/projects.ts (1)
196-206: Avoidanyfor update payload; use a typed partial.Improves type safety for updates.
Apply this diff:
- const updateData: any = {}; + type ProjectUpdate = { + cachingEnabled?: boolean; + cacheDurationSeconds?: number; + mode?: "api-keys" | "credits" | "hybrid"; + }; + const updateData: ProjectUpdate = {};apps/ui/src/app/layout.tsx (1)
1-3: Prefer config over inline disable for CSS side‑effect import.Keeping the CSS import at the top is fine for Next.js, but avoid per-file disables by teaching import/order about CSS. Example flat-config snippet:
+// eslint.config.mjs (excerpt) +import importPlugin from "eslint-plugin-import"; + +export default [ + { + plugins: { import: importPlugin }, + rules: { + "import/order": ["error", { + "pathGroups": [ + { pattern: "**/*.css", group: "unknown", position: "before" } + ], + "pathGroupsExcludedImportTypes": ["builtin"] + }] + } + } +];If you keep the directive, limit it to a single line as done here.
apps/ui/src/components/dashboard/new-project-dialog.tsx (1)
71-91: Harden error handling in onError.
erroris oftenunknown; accessing.messagecan be unsafe. Normalize first.- } else { - // Generic error toast - toast({ - title: "Failed to create project", - description: - error.message || "An unexpected error occurred. Please try again.", - variant: "destructive", - }); - } + } else { + const msg = + error instanceof Error ? error.message : String(error ?? "Unknown error"); + toast({ + title: "Failed to create project", + description: msg || "An unexpected error occurred. Please try again.", + variant: "destructive", + }); + }apps/ui/src/app/dashboard/[orgId]/[projectId]/settings/billing/page.tsx (1)
18-26: Page props should not be Promises.Next.js passes
params/searchParamsas plain objects. Typing them asPromise<...>is misleading.-interface BillingPageProps { - params: Promise<{ - orgId: string; - projectId: string; - }>; - searchParams: Promise<{ - success?: string; - canceled?: string; - }>; -} +interface BillingPageProps { + params: { orgId: string; projectId: string }; + searchParams: { success?: string; canceled?: string }; +}Also drop the
awaitwhen destructuring them.apps/api/src/lib/beacon.ts (1)
44-46: Nit: unmatched parenthesis in log message.Close the opening parenthesis.
- logger.info( - "Sending installation beacon (for anonymous tracking of self-hosted installs. To disable, set TELEMETRY_ACTIVE=false in your environment variables.", - ); + logger.info( + "Sending installation beacon (for anonymous tracking of self-hosted installs). To disable, set TELEMETRY_ACTIVE=false in your environment variables.", + );apps/gateway/src/lib/costs.ts (1)
7-11: Optional: include 'tool' role for future‑proofing.Tokenizer/chat ecosystems frequently use a 'tool' role.
-interface ChatMessage { - role: "user" | "system" | "assistant" | undefined; +interface ChatMessage { + role: "user" | "system" | "assistant" | "tool" | undefined;apps/ui/src/components/providers/hero.tsx (1)
52-58: Add rel to external link opened in new tab.Prevent tab‑nabbing.
- <a - href={`${provider.website}?utm_source=llmgateway-models`} - target="_blank" - > + <a + href={`${provider.website}?utm_source=llmgateway-models`} + target="_blank" + rel="noopener noreferrer" + >apps/ui/src/app/changelog/page.tsx (1)
4-5: Fix import order to satisfy import/order.Place external package imports before internal alias imports to resolve the failing lint check.
Apply this diff:
-import { ChangelogComponent } from "@/components/changelog"; -import { HeroRSC } from "@/components/landing/hero-rsc"; - -import { allChangelogs, type Changelog } from "content-collections"; +import { allChangelogs, type Changelog } from "content-collections"; +import { ChangelogComponent } from "@/components/changelog"; +import { HeroRSC } from "@/components/landing/hero-rsc";apps/ui/src/components/landing/footer.tsx (1)
9-10: Confirm grouping for workspace imports (@llmgateway/models) vs internal (@/...)With typical import/order groups (builtin, external, internal), a workspace package is “external” and would be placed above "@/…". If your eslint config treats @llmgateway/* as internal, this is fine; otherwise consider moving it into the external group.
apps/ui/src/app/providers/[id]/page.tsx (1)
8-12: Check import-order grouping for monorepo packagesSimilar to footer.tsx: if @llmgateway/* is configured as internal, this order is correct; if not, import/order may expect it in the external group above "@/…".
apps/ui/src/app/blog/[slug]/page.tsx (1)
11-12: Fix import/order: placecontent-collectionsin the external group (before lucide-react) and alphabetize
import/orderexpects external imports first and sorted;content-collectionsshould be abovelucide-react. Move this import into the external block.Apply this diff to remove from the current position:
- import { allBlogs, type Blog } from "content-collections";Then add it at the top external group (example target ordering):
import { allBlogs, type Blog } from "content-collections"; import { ArrowLeftIcon } from "lucide-react"; import Markdown from "markdown-to-jsx"; import Image from "next/image"; import Link from "next/link"; import { notFound } from "next/navigation"; import Footer from "@/components/landing/footer"; import { HeroRSC } from "@/components/landing/hero-rsc"; import { getMarkdownOptions } from "@/lib/utils/markdown";apps/ui/src/app/changelog/[slug]/page.tsx (1)
11-12: Fix import/order: movecontent-collectionsto the external group and sortLike the blog page, place this external import before
lucide-reactand other internals.Apply this diff to remove from the current position:
- import { allChangelogs, type Changelog } from "content-collections";Then add to the top external block (example):
import { allChangelogs, type Changelog } from "content-collections"; import { ArrowLeftIcon } from "lucide-react"; import Markdown from "markdown-to-jsx"; import Image from "next/image"; import Link from "next/link"; import { notFound } from "next/navigation"; import Footer from "@/components/landing/footer"; import { HeroRSC } from "@/components/landing/hero-rsc"; import { getMarkdownOptions } from "@/lib/utils/markdown";apps/ui/src/components/api-keys/create-api-key-dialog.tsx (1)
23-23: Minor type-import polishConsider importing named types to avoid
React.prefixes.-import type React from "react"; +import type { ReactNode, FormEvent } from "react";And update usages:
children: ReactNodeconst handleSubmit = (e: FormEvent) => { … }apps/ui/src/components/onboarding/provider-key-step.tsx (1)
23-35: Normalize import paths to alias to satisfy import rulesThis file mixes "@/..." and relative "../../..." paths, which will trip import/no-useless-path-segments and import/order with the new plugins. Switch to alias paths for consistency.
Apply:
-import { Button } from "../../lib/components/button"; +import { Button } from "@/lib/components/button"; @@ -} from "../../lib/components/card"; +} from "@/lib/components/card"; @@ -import { Step } from "../../lib/components/stepper"; +import { Step } from "@/lib/components/stepper"; @@ -import { ProviderSelect } from "../provider-keys/provider-select"; +import { ProviderSelect } from "@/components/provider-keys/provider-select";apps/ui/src/app/models/[name]/page.tsx (1)
27-33: Nit: params typing doesn’t need to be a PromiseNext.js passes
paramsas a plain object. Consider simplifying the prop type and dropping theawait.-interface PageProps { - params: Promise<{ name: string }>; -} +interface PageProps { + params: { name: string }; +} @@ -export default async function ModelPage({ params }: PageProps) { - const { name } = await params; +export default async function ModelPage({ params }: PageProps) { + const { name } = params;apps/api/src/routes/keys-provider.ts (1)
171-175: Nit: error message says “project” but scope is organizationThis endpoint de-duplicates per organization. Consider adjusting wording for clarity.
- message: `A key for provider '${provider}' already exists for this project`, + message: `A key for provider '${provider}' already exists for this organization`,apps/api/src/routes/keys-provider.e2e.ts (1)
158-199: Remove duplicate skipped test blockThere are two identical
test.skip("POST /keys/provider with custom baseUrl", ...)blocks. Keep one to reduce noise.- test.skip("POST /keys/provider with custom baseUrl", async () => { - // ...duplicate content... - });apps/ui/src/components/dashboard/new-organization-dialog.tsx (1)
19-19: Type‑only React import is appropriate; consider narrowing the event type.Optional nit: type the submit handler as
React.FormEvent<HTMLFormElement>for stricter inference.-const handleSubmit = async (e: React.FormEvent) => { +const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {apps/api/src/index.ts (1)
1-3: Top-level dotenv load is correct; avoid per-file ESLint disable via config.Side-effect import is appropriate here. Prefer configuring import/order to allow it instead of an inline disable.
Add this to eslint.config.mjs (flat config) so side‑effect imports like "dotenv/config" are accepted at the top without disable:
// inside your shared config object settings: { 'import/resolver': { typescript: { // point to all tsconfigs in the monorepo project: ['apps/*/tsconfig.json', 'packages/*/tsconfig.json'], alwaysTryTypes: true, }, node: { extensions: ['.ts', '.tsx', '.js', '.jsx'] }, }, }, rules: { 'import/order': ['error', { groups: ['type', 'builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'unknown'], pathGroups: [ { pattern: 'dotenv/config', group: 'builtin', position: 'before' }, ], pathGroupsExcludedImportTypes: ['builtin'], 'newlines-between': 'always', alphabetize: { order: 'asc', caseInsensitive: true }, }], }
| import { db } from "@llmgateway/db"; | ||
| import { logger } from "@llmgateway/logger"; | ||
|
|
There was a problem hiding this comment.
Heads-up: resolver errors in CI need addressing (EACCES from eslint-import-resolver-typescript).
Static analysis shows EACCES from get-tsconfig via the TS resolver when rules like import/order run. Likely a resolver/tsconfig discovery issue in CI.
Try:
- Pin and/or upgrade resolver to a stable version and ensure projects are explicit:
- "eslint-import-resolver-typescript": "^3.7.0" or latest 4.x known-good in your CI.
- In eslint.config.mjs, set resolver project globs (apps/, packages/) as above.
- Ensure ESLint runs with repo root as CWD; if not, add workingDirectories in editor and CI.
- If the CI still fails, temporarily limit resolver usage by disabling rules that invoke resolution (e.g., import/no-unresolved, import/no-useless-path-segments) until resolver is stable.
I can generate a minimal CI repro script to iterate resolver versions if helpful.
🤖 Prompt for AI Agents
In apps/api/src/index.ts around lines 10-12 the CI is failing with EACCES from
the TypeScript import resolver during ESLint import rules; to fix it pin or
upgrade eslint-import-resolver-typescript in package.json to a known-good
version (e.g., "^3.7.0" or a stable 4.x), update eslint.config.mjs to provide
explicit resolver.project globs covering your monorepo (e.g., "apps/*",
"packages/*"), ensure ESLint is executed with the repository root as CWD in CI
(or set workingDirectories in the CI/editor), and if CI still fails temporarily
disable resolver-heavy rules (import/no-unresolved,
import/no-useless-path-segments) until the resolver version is stabilized.
| import { db } from "@llmgateway/db"; | ||
| import { logger } from "@llmgateway/logger"; | ||
|
|
||
| import { ensureStripeCustomer } from "../stripe"; | ||
| import { stripe } from "./payments"; | ||
|
|
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Import reshuffle looks good; check for circular deps with payments.
Bringing in stripe from "./payments" alongside ensureStripeCustomer is fine, but confirm there’s no cycle (payments → subscriptions).
🏁 Script executed:
#!/bin/bash
rg -nP 'from\s+"\.\/subscriptions"' apps/api/src/routes/payments.ts || true
rg -nP 'from\s+"\.{1,2}\/stripe"' apps/api/src/routes/{payments,subscriptions}.tsLength of output: 172
🏁 Script executed:
#!/bin/bash
set -uo pipefail
echo "1) List any stripe*.ts files under apps/"
rg -n --hidden -S --glob '!**/node_modules/**' -g 'apps/**/src/**/stripe*.ts' || true
echo
echo "2) Search for ensureStripeCustomer definition/usage across apps/"
rg -n --hidden -S --glob '!**/node_modules/**' 'ensureStripeCustomer' apps || true
echo
echo "3) Show top of subscriptions.ts and payments.ts to confirm imports"
if [[ -f apps/api/src/routes/subscriptions.ts ]]; then
echo "---- apps/api/src/routes/subscriptions.ts ----"
sed -n '1,120p' apps/api/src/routes/subscriptions.ts || true
else
echo "subscriptions.ts not found"
fi
echo
if [[ -f apps/api/src/routes/payments.ts ]]; then
echo "---- apps/api/src/routes/payments.ts ----"
sed -n '1,160p' apps/api/src/routes/payments.ts || true
else
echo "payments.ts not found"
fi
echo
echo "4) Print stripe module file if present at apps/api/src/stripe.ts or apps/api/src/stripe/index.ts"
if [[ -f apps/api/src/stripe.ts ]]; then
echo "---- apps/api/src/stripe.ts ----"
sed -n '1,300p' apps/api/src/stripe.ts || true
elif [[ -f apps/api/src/stripe/index.ts ]]; then
echo "---- apps/api/src/stripe/index.ts ----"
sed -n '1,300p' apps/api/src/stripe/index.ts || true
else
echo "stripe module not found at apps/api/src/stripe.ts or apps/api/src/stripe/index.ts"
fiLength of output: 15276
Break circular import between payments and stripe
Found cycle: apps/api/src/routes/payments.ts imports ensureStripeCustomer from ../stripe, while apps/api/src/stripe.ts imports { stripe } from ./routes/payments — this creates a payments ↔ stripe circular dependency and can produce partially-initialized exports at runtime.
Fix: break the cycle — either move the Stripe client initialization into apps/api/src/stripe.ts and have payments/subscriptions import it from there (update payments.ts/subscriptions.ts to import stripe from ../stripe), or extract ensureStripeCustomer into a small shared module both can import. Also remove the reciprocal import in apps/api/src/stripe.ts (stop importing from ./routes/payments).
🤖 Prompt for AI Agents
In apps/api/src/routes/subscriptions.ts around lines 5 to 10 there is a circular
import: routes/payments.ts imports ensureStripeCustomer from ../stripe while
stripe.ts imports { stripe } from ./routes/payments, leading to
partially-initialized exports at runtime; break the cycle by moving Stripe
client initialization into apps/api/src/stripe.ts and update payments.ts and
subscriptions.ts to import the stripe client from ../stripe (or alternatively
extract ensureStripeCustomer into a small shared module both can import), and
remove any reciprocal import from apps/api/src/stripe.ts so stripe.ts no longer
imports from ./routes/payments.
| // eslint-disable-next-line import/order | ||
| import "./global.css"; |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Address resolver EACCES failures in CI by scoping TS project globs
Static analysis shows EACCES from eslint-import-resolver-typescript when auto-discovering tsconfigs. Explicitly set resolver projects to workspace globs to avoid tmp writes.
Add to eslint.config.mjs (or equivalent):
export default [
{
settings: {
'import/resolver': {
- typescript: {},
+ typescript: {
+ alwaysTryTypes: true,
+ project: [
+ 'apps/*/tsconfig.json',
+ 'packages/*/tsconfig.json',
+ 'tools/*/tsconfig.json'
+ ],
+ },
+ node: true,
},
},
},
];Then verify locally:
🏁 Script executed:
#!/bin/bash
# Run ESLint with resolver debug to ensure no EACCES and paths resolve.
export DEBUG=eslint-plugin-import:resolver:typescript,eslint-module-utils:resolve
pnpm exec eslint apps/docs/app/layout.tsxLength of output: 4480
Fix EACCES in CI: scope TypeScript resolver projects in ESLint
ESLint reproduced EACCES errors when auto-discovering tsconfigs; lock the import/resolver typescript project to workspace globs to avoid tmp writes. Edit eslint.config.mjs (or equivalent):
export default [
{
settings: {
'import/resolver': {
- typescript: {},
+ typescript: {
+ alwaysTryTypes: true,
+ project: [
+ 'apps/*/tsconfig.json',
+ 'packages/*/tsconfig.json',
+ 'tools/*/tsconfig.json'
+ ],
+ },
+ node: true,
},
},
},
];Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 ESLint
[error] 1-1: Resolve error: EACCES: permission denied, open '/OzKKBLsyBT'
at Object.writeFileSync (node:fs:2409:20)
at l (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:13685)
at createFilesMatcher (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:14437)
at Object.resolve (/home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import@2.32.0_eslint@9.34.0_jiti@2.5.1_/node_modules/eslint-import-resolver-typescript/lib/index.cjs:298:107)
at withResolver (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:180:23)
at fullResolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:201:22)
at relative (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:217:10)
at resolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:233:12)
at resolveImportType (/home/jailuser/git/node_modules/.pnpm/eslint-plugin-import@2.32.0_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1___504bceda7a403f00735dcb51d6a813c7/node_modules/eslint-plugin-import/lib/core/importType.js:126:2822)
at computeRank (/home/jailuser/git/node_modules/.pnpm/eslint-plugin-import@2.32.0_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1___504bceda7a403f00735dcb51d6a813c7/node_modules/eslint-plugin-import/lib/rules/order.js:529:43)
(import/order)
[error] 1-1: Resolve error: EACCES: permission denied, open '/cbXESOWCNQ'
at Object.writeFileSync (node:fs:2409:20)
at l (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:13685)
at createFilesMatcher (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:14437)
at Object.resolve (/home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import@2.32.0_eslint@9.34.0_jiti@2.5.1_/node_modules/eslint-import-resolver-typescript/lib/index.cjs:298:107)
at withResolver (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:180:23)
at fullResolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:201:22)
at relative (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:217:10)
at resolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:233:12)
at checkSourceValue (/home/jailuser/git/node_modules/.pnpm/eslint-plugin-import@2.32.0_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1___504bceda7a403f00735dcb51d6a813c7/node_modules/eslint-plugin-import/lib/rules/no-useless-path-segments.js:85:53)
at checkSourceValue (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/moduleVisitor.js:32:5)
(import/no-useless-path-segments)
🤖 Prompt for AI Agents
In apps/docs/app/layout.tsx around lines 1-2: ESLint's TypeScript resolver is
auto-discovering tsconfig files and causing EACCES in CI; update your ESLint
config (e.g., eslint.config.mjs) to explicitly scope the import/resolver
typescript "project" option to workspace globs (for example the repo tsconfig
paths like "apps/*/tsconfig.json" and "packages/*/tsconfig.json" or the exact
tsconfig locations used by this monorepo) instead of leaving it unset so the
resolver doesn't scan tmp dirs; save and re-run CI to confirm the EACCES is
gone.
| import { describe, expect, test } from "vitest"; | ||
|
|
||
| import { app } from "../index"; | ||
| import { app } from ".."; |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Import root shortcut looks fine; verify resolver handles directory imports.
Switching from "../index" to ".." is OK with Vitest/Vite resolution. With the new eslint-plugin-import + TS resolver, make sure directory imports aren’t flagged as unresolved in CI.
Run this to confirm no import resolution errors are reported on this file:
🏁 Script executed:
#!/bin/bash
rg -n 'import\s+\{\s*app\s*\}\s+from\s+"\.\."' apps/gateway/src/models/models.spec.ts
pnpm -w eslint apps/gateway/src/models/models.spec.ts || trueLength of output: 4522
Import root shortcut OK — ESLint resolver failing (EACCES); fix CI permissions/config
- Running the supplied eslint command produced Resolve error: EACCES: permission denied, open '/HVPkYalrcW' and '/oMygdkRBlX' (from get-tsconfig / eslint-import-resolver-typescript), which prevents verifying that the ".." directory import isn’t flagged.
- Action: ensure the linter can write to a writable temp dir in CI (set TMPDIR or fix container permissions) or configure import/resolver/typescript with an explicit
project(pointing to the repo/app tsconfig(s)) to avoid get-tsconfig creating temp files; re-run eslint and confirm no unresolved-directory-import errors.
🤖 Prompt for AI Agents
In apps/gateway/src/models/models.spec.ts around line 3, ESLint import
resolution is failing with EACCES when get-tsconfig attempts to create temp
files for the ".." import; fix by making the linter able to write temp files in
CI (set TMPDIR to a writable directory or adjust container filesystem
permissions) or, preferably, configure eslint-import-resolver-typescript with an
explicit "project" pointing to the relevant tsconfig.json(s) for the repo/apps
so get-tsconfig doesn't create temp files; update the ESLint resolver config,
ensure CI has the writable temp dir (or set TMPDIR in CI), re-run eslint and
verify the unresolved-directory-import error is gone.
| @@ -1,4 +1,6 @@ | |||
| // eslint-disable-next-line import/order | |||
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Resolver EACCES from eslint-import-resolver-typescript — verify resolver config.
CI hint shows get-tsconfig attempting FS writes causing EACCES. Ensure resolver is scoped to workspace tsconfigs and not probing root. Confirm with:
Common fixes:
- Set resolver projects explicitly: { typescript: { project: ["tsconfig.json","apps//tsconfig.json","packages//tsconfig.json"] } }
- Ensure parserOptions.tsconfigRootDir is repo root.
🏁 Script executed:
#!/bin/bash
# Print resolved ESLint config for one of the affected files
pnpm -w eslint --print-config apps/ui/src/app/layout.tsx | jq '."settings"."import/resolver"'
# Run lint in verbose to surface resolver issues
DEBUG=eslint-plugin-import:* pnpm -w eslint apps/ui --ext .ts,.tsxLength of output: 524286
Fix eslint-import-resolver-typescript EACCES: scope resolver projects & set tsconfigRootDir
Print-config shows import/resolver.typescript uses globs (["./tsconfig.json","./apps//tsconfig.json","./packages//tsconfig.json"]) and the lint run shows get-tsconfig is trying to write temp files at root (EACCES). Actionable fixes:
- Replace broad/globbed projects with explicit repo tsconfig paths (or use a single composite tsconfig with references) in settings.import/resolver.typescript.project.
- Ensure parserOptions.tsconfigRootDir is set to the repository root (e.g. parserOptions: { tsconfigRootDir: __dirname } in your .eslintrc.js/.cjs) so the resolver resolves relative to the workspace instead of probing/creating files at /.
- If you must support multiple tsconfigs, consider using a single referenced tsconfig, set typescript.noWarnOnMultipleProjects, or ensure CI’s TMPDIR is writable as a temporary workaround.
🧰 Tools
🪛 ESLint
[error] 1-1: Resolve error: EACCES: permission denied, open '/zslAFUfEHV'
at Object.writeFileSync (node:fs:2409:20)
at l (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:13685)
at createFilesMatcher (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:14437)
at Object.resolve (/home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import@2.32.0_eslint@9.34.0_jiti@2.5.1_/node_modules/eslint-import-resolver-typescript/lib/index.cjs:298:107)
at withResolver (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:180:23)
at fullResolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:201:22)
at relative (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:217:10)
at resolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:233:12)
at resolveImportType (/home/jailuser/git/node_modules/.pnpm/eslint-plugin-import@2.32.0_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1___504bceda7a403f00735dcb51d6a813c7/node_modules/eslint-plugin-import/lib/core/importType.js:126:2822)
at computeRank (/home/jailuser/git/node_modules/.pnpm/eslint-plugin-import@2.32.0_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1___504bceda7a403f00735dcb51d6a813c7/node_modules/eslint-plugin-import/lib/rules/order.js:529:43)
(import/order)
[error] 1-1: Resolve error: EACCES: permission denied, open '/MsbzaujiEK'
at Object.writeFileSync (node:fs:2409:20)
at l (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:13685)
at createFilesMatcher (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:14437)
at Object.resolve (/home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import@2.32.0_eslint@9.34.0_jiti@2.5.1_/node_modules/eslint-import-resolver-typescript/lib/index.cjs:298:107)
at withResolver (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:180:23)
at fullResolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:201:22)
at relative (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:217:10)
at resolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:233:12)
at checkSourceValue (/home/jailuser/git/node_modules/.pnpm/eslint-plugin-import@2.32.0_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1___504bceda7a403f00735dcb51d6a813c7/node_modules/eslint-plugin-import/lib/rules/no-useless-path-segments.js:85:53)
at checkSourceValue (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/moduleVisitor.js:32:5)
(import/no-useless-path-segments)
🤖 Prompt for AI Agents
In apps/ui/src/app/layout.tsx around lines 1 to 1, the repo-level ESLint config
is causing the typescript resolver to probe/create temp tsconfig files at the
filesystem root (EACCES) because projects are specified with broad globs and
tsconfigRootDir isn’t set; update your ESLint/TypeScript config to list explicit
tsconfig paths (or use one composite referenced tsconfig) in
settings["import/resolver"].typescript.project, set
parserOptions.tsconfigRootDir to the repository root (e.g. __dirname in the
.eslintrc.* file) so resolution is rooted to the workspace, and if multiple
tsconfigs are required either create a single referenced tsconfig, enable
typescript.noWarnOnMultipleProjects, or ensure CI TMPDIR is writable as a
temporary workaround.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
apps/api/src/routes/keys-provider.ts (1)
156-176: Add unique DB constraint on (organizationId, provider) and make inserts conflict-safepackages/db/src/schema.ts already defines unique().on(table.organizationId, table.name) (~line 272) but there is no unique constraint on (organizationId, provider). Add a unique().on(table.organizationId, table.provider) in the schema and change the insert(s) to use an atomic ON CONFLICT targeting those columns. Update both insert locations in apps/api/src/routes/keys-provider.ts (~156–176 and ~220–229).
Suggested insert change:
const [providerKey] = await db .insert(tables.providerKey) + .onConflictDoNothing({ + target: [tables.providerKey.organizationId, tables.providerKey.provider], + }) .values({ token: userToken, organizationId, provider, name, baseUrl, }) .returning();apps/ui/src/components/landing/hero.tsx (2)
146-149: Add rel="noopener noreferrer" to external link with target="_blank".Prevents reverse‑tabnabbing; also prefer avoiding empty hrefs.
- <a + <a href={config.githubUrl ?? ""} - target="_blank" + target="_blank" rel="noopener noreferrer" className="mx-auto lg:mx-0 hover:bg-background dark:hover:border-t-border bg-muted group flex w-fit items-center gap-4 rounded-full border p-1 pl-4 shadow-md shadow-black/5 transition-all duration-300 dark:border-t-white/5 dark:shadow-zinc-950" >Optionally avoid empty hrefs:
- href={config.githubUrl ?? ""} + href={config.githubUrl || undefined}
221-226: Also add rel on the docs link opened in a new tab.Same security concern as above.
- <a href={config.docsUrl ?? ""} target="_blank"> + <a href={config.docsUrl ?? ""} target="_blank" rel="noopener noreferrer">
🧹 Nitpick comments (14)
apps/api/src/routes/keys-provider.ts (2)
5-7: Optionally de-duplicate '@llmgateway/models' imports.If
import/no-duplicatesis configured to flag type/value duplicates, merge into a single statement.-import { providers, validateProviderKey } from "@llmgateway/models"; +import { providers, validateProviderKey, type ProviderId } from "@llmgateway/models"; - -import type { ProviderId } from "@llmgateway/models";Also applies to: 11-12
307-313: Avoid spreading the raw token then overriding withundefined.Prevents accidental leakage and aligns the shape with the schema without relying on JSON.stringify behavior.
-return c.json({ - providerKeys: providerKeys.map((key) => ({ - ...key, - maskedToken: maskToken(key.token), - token: undefined, - })), -}); +return c.json({ + providerKeys: providerKeys.map(({ token, ...rest }) => ({ + ...rest, + maskedToken: maskToken(token), + })), +});packages/cache/src/cache.ts (3)
1-1: Prefernode:specifier and a named import from crypto.Improves clarity, avoids polyfill ambiguity, and typically plays nicer with bundlers and
eslint-plugin-importresolution.Apply this diff:
-import crypto from "crypto"; +import { createHash } from "node:crypto";And update usage:
- return crypto - .createHash("sha256") + return createHash("sha256") .update(JSON.stringify(payload)) .digest("hex");
3-9: Consolidate imports from @llmgateway/db and mark types as type‑only.Reduces duplicate module loads and aligns with
import/no-duplicatesandimport/orderwhen using TS type imports.Apply this diff:
-import { db, type InferSelectModel } from "@llmgateway/db"; +import { db } from "@llmgateway/db"; +import type { InferSelectModel, tables } from "@llmgateway/db"; -import type { tables } from "@llmgateway/db";
10-15: Cache key may vary with object key order; consider stable stringify.If callers pass semantically identical payloads with different key orders, keys will differ. Optional improvement.
If you want this, add a stable stringify helper and use it:
+function stableStringify(value: unknown): string { + if (value === null || typeof value !== "object") return JSON.stringify(value); + if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`; + const entries = Object.entries(value as Record<string, unknown>).sort(([a],[b]) => a.localeCompare(b)); + return `{${entries.map(([k,v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(",")}}`; +} export function generateCacheKey(payload: Record<string, any>): string { - return crypto - .createHash("sha256") - .update(JSON.stringify(payload)) + return createHash("sha256") + .update(stableStringify(payload)) .digest("hex"); }apps/ui/src/components/landing/hero.tsx (1)
379-385: Nit: drop explicit prefetch unless you have a measured need.Reduces unnecessary network work; Next.js defaults are generally sufficient.
- <Link - href="/providers" - className="block text-sm duration-150 hover:opacity-75" - prefetch={true} - > + <Link + href="/providers" + className="block text-sm duration-150 hover:opacity-75" + >eslint.config.mjs (2)
8-22: Resolver config: add PNPM support and internal-regex; consider Node resolver + performance.
- Add
import/external-module-foldersto handle PNPM’s.pnpmlayout.- Mark
@/and@llmgateway/as internal viaimport/internal-regex(keeps grouping stable without relying only on pathGroups).- Optionally add
noderesolver with extensions for mixed ESM/TS.- Large
projectglobs can slow lint; if you hit perf issues, consider a centraltsconfig.eslint.jsonor narrower globs.Apply within this block:
settings: { "import/resolver": { typescript: { alwaysTryTypes: true, project: [ "./tsconfig.json", "./apps/*/tsconfig.json", "./packages/*/tsconfig.json", ], }, + node: { + extensions: [".js", ".mjs", ".ts", ".tsx", ".d.ts"], + }, }, + "import/internal-regex": "^(@|@llmgateway)/", + "import/external-module-folders": ["node_modules", "node_modules/.pnpm"], },
85-98: Disabling no-console for entire UI/Docs is risky; scope to tests.Turning it off across
apps/ui/**andapps/docs/**can mask production logs. Limit to test files, or downgrade to “warn” for UI/Docs.Apply this minimal change now:
files: [ "**/*.spec.ts", "**/*.spec.tsx", "**/*.test.ts", "**/*.test.tsx", "**/*.e2e.ts", "**/test-utils/**", - "apps/ui/**", - "apps/docs/**", ],Then add another override (outside this hunk) if desired:
{ files: ["apps/ui/**", "apps/docs/**"], rules: { "no-console": "warn" }, },apps/ui/src/app/blog/[slug]/page.tsx (3)
15-21: Fix Next.js props typing: params is not a Promise.Next passes
paramsas a plain object. Align the type and drop the unnecessaryawait.-interface BlogEntryPageProps { - params: Promise<{ slug: string }>; -} +interface BlogEntryPageProps { + params: { slug: string }; +} export default async function BlogEntryPage({ params }: BlogEntryPageProps) { - const { slug } = await params; + const { slug } = params;
51-57: Stabilize date rendering across time zones.If
entry.dateisYYYY-MM-DD, local TZ can shift it by a day. Force UTC in formatting.- {new Date(entry.date).toLocaleDateString("en-US", { + {new Date(entry.date).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric", + timeZone: "UTC", })}
111-116: Use nullish coalescing for numeric fallbacks.Avoid treating 0 as falsy; use
??.- width: entry.image.width || 800, - height: entry.image.height || 400, + width: entry.image.width ?? 800, + height: entry.image.height ?? 400,apps/ui/src/app/changelog/[slug]/page.tsx (3)
15-23: Fix Next.js props typing: params should not be Promise.Align with Next’s Page props and drop the
await.-interface ChangelogEntryPageProps { - params: Promise<{ slug: string }>; -} +interface ChangelogEntryPageProps { + params: { slug: string }; +} export default async function ChangelogEntryPage({ params, }: ChangelogEntryPageProps) { - const { slug } = await params; + const { slug } = params;
55-60: Same TZ stability nit: force UTC when formatting dates.- {new Date(entry.date).toLocaleDateString("en-US", { + {new Date(entry.date).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric", + timeZone: "UTC", })}
115-118: Prefer??over||for numeric defaults.- width: entry.image.width || 800, - height: entry.image.height || 400, + width: entry.image.width ?? 800, + height: entry.image.height ?? 400,
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (19)
apps/api/src/routes/keys-provider.ts(1 hunks)apps/ui/src/app/blog/[slug]/page.tsx(2 hunks)apps/ui/src/app/changelog/[slug]/page.tsx(2 hunks)apps/ui/src/app/changelog/page.tsx(1 hunks)apps/ui/src/app/onboarding/page.tsx(1 hunks)apps/ui/src/app/playground/page.tsx(1 hunks)apps/ui/src/components/activity/recent-logs.tsx(1 hunks)apps/ui/src/components/api-keys/api-keys-list.tsx(1 hunks)apps/ui/src/components/dashboard/dashboard-sidebar.tsx(1 hunks)apps/ui/src/components/dashboard/organization-switcher.tsx(1 hunks)apps/ui/src/components/dashboard/project-switcher.tsx(1 hunks)apps/ui/src/components/landing/hero.tsx(1 hunks)apps/ui/src/components/models/all-models.tsx(1 hunks)apps/ui/src/components/models/models-list.tsx(1 hunks)apps/ui/src/components/provider-keys/create-provider-key-dialog.tsx(1 hunks)apps/ui/src/components/provider-keys/provider-keys-list.tsx(1 hunks)apps/ui/src/components/usage/cost-breakdown-chart.tsx(1 hunks)eslint.config.mjs(3 hunks)packages/cache/src/cache.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (11)
- apps/ui/src/components/provider-keys/provider-keys-list.tsx
- apps/ui/src/components/provider-keys/create-provider-key-dialog.tsx
- apps/ui/src/components/models/models-list.tsx
- apps/ui/src/components/dashboard/project-switcher.tsx
- apps/ui/src/components/activity/recent-logs.tsx
- apps/ui/src/app/playground/page.tsx
- apps/ui/src/app/onboarding/page.tsx
- apps/ui/src/components/api-keys/api-keys-list.tsx
- apps/ui/src/components/usage/cost-breakdown-chart.tsx
- apps/ui/src/components/dashboard/dashboard-sidebar.tsx
- apps/ui/src/components/dashboard/organization-switcher.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Always use top-level import; never use require() or dynamic import()
Always use top-level import; never use require or dynamic imports
Files:
apps/ui/src/components/landing/hero.tsxapps/ui/src/components/models/all-models.tsxapps/api/src/routes/keys-provider.tsapps/ui/src/app/blog/[slug]/page.tsxapps/ui/src/app/changelog/[slug]/page.tsxapps/ui/src/app/changelog/page.tsxpackages/cache/src/cache.ts
apps/ui/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/ui/**/*.{ts,tsx}: In the Next.js UI, use next/link for links and next/navigation's router for programmatic navigation
Use cookies for user settings that aren’t stored in the database to ensure SSR worksUse cookies for user-settings not stored in the database to ensure SSR works in the UI
Files:
apps/ui/src/components/landing/hero.tsxapps/ui/src/components/models/all-models.tsxapps/ui/src/app/blog/[slug]/page.tsxapps/ui/src/app/changelog/[slug]/page.tsxapps/ui/src/app/changelog/page.tsx
apps/ui/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use next/link for links and next/navigation's router for programmatic navigation in the Next.js UI app
Files:
apps/ui/src/components/landing/hero.tsxapps/ui/src/components/models/all-models.tsxapps/ui/src/app/blog/[slug]/page.tsxapps/ui/src/app/changelog/[slug]/page.tsxapps/ui/src/app/changelog/page.tsx
{apps/{api,gateway}/**/*.ts,packages/db/**/*.ts}
📄 CodeRabbit inference engine (CLAUDE.md)
.findMany() or db().query.
{apps/{api,gateway}/**/*.ts,packages/db/**/*.ts}: For database reads, use Drizzle’s db().query..findFirst()
Use Drizzle ORM with the latest object syntaxFiles:
apps/api/src/routes/keys-provider.tsapps/{api,gateway}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
apps/{api,gateway}/**/*.{ts,tsx}: Use Drizzle ORM with the latest object syntax for database access
For reads, use db().query..findMany() or db().query.
.findFirst()
After API route changes, run pnpm generate to update OpenAPI schemasFiles:
apps/api/src/routes/keys-provider.ts🧠 Learnings (1)
📚 Learning: 2025-09-13T16:25:00.704Z
Learnt from: CR PR: theopenco/llmgateway#0 File: CLAUDE.md:0-0 Timestamp: 2025-09-13T16:25:00.704Z Learning: Applies to **/*.{ts,tsx,js,jsx} : Always use top-level import; never use require() or dynamic import()Applied to files:
eslint.config.mjs🪛 ESLint
apps/ui/src/app/blog/[slug]/page.tsx
[error] 1-1: Resolve error: EACCES: permission denied, open '/YsFKGGajyO'
at Object.writeFileSync (node:fs:2409:20)
at l (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:13685)
at createFilesMatcher (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:14437)
at Object.resolve (/home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import@2.32.0_eslint@9.34.0_jiti@2.5.1_/node_modules/eslint-import-resolver-typescript/lib/index.cjs:298:107)
at withResolver (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:180:23)
at fullResolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:201:22)
at relative (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:217:10)
at resolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:233:12)
at resolveImportType (/home/jailuser/git/node_modules/.pnpm/eslint-plugin-import@2.32.0_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1___504bceda7a403f00735dcb51d6a813c7/node_modules/eslint-plugin-import/lib/core/importType.js:126:2822)
at computeRank (/home/jailuser/git/node_modules/.pnpm/eslint-plugin-import@2.32.0_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1___504bceda7a403f00735dcb51d6a813c7/node_modules/eslint-plugin-import/lib/rules/order.js:529:43)(import/order)
apps/ui/src/app/changelog/[slug]/page.tsx
[error] 1-1: Resolve error: EACCES: permission denied, open '/wgBhZjlNky'
at Object.writeFileSync (node:fs:2409:20)
at l (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:13685)
at createFilesMatcher (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:14437)
at Object.resolve (/home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import@2.32.0_eslint@9.34.0_jiti@2.5.1_/node_modules/eslint-import-resolver-typescript/lib/index.cjs:298:107)
at withResolver (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:180:23)
at fullResolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:201:22)
at relative (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:217:10)
at resolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:233:12)
at resolveImportType (/home/jailuser/git/node_modules/.pnpm/eslint-plugin-import@2.32.0_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1___504bceda7a403f00735dcb51d6a813c7/node_modules/eslint-plugin-import/lib/core/importType.js:126:2822)
at computeRank (/home/jailuser/git/node_modules/.pnpm/eslint-plugin-import@2.32.0_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1___504bceda7a403f00735dcb51d6a813c7/node_modules/eslint-plugin-import/lib/rules/order.js:529:43)(import/order)
apps/ui/src/app/changelog/page.tsx
[error] 1-1: Resolve error: EACCES: permission denied, open '/NdjbLSIpnP'
at Object.writeFileSync (node:fs:2409:20)
at l (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:13685)
at createFilesMatcher (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:14437)
at Object.resolve (/home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import@2.32.0_eslint@9.34.0_jiti@2.5.1_/node_modules/eslint-import-resolver-typescript/lib/index.cjs:298:107)
at withResolver (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:180:23)
at fullResolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:201:22)
at relative (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:217:10)
at resolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:233:12)
at resolveImportType (/home/jailuser/git/node_modules/.pnpm/eslint-plugin-import@2.32.0_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1___504bceda7a403f00735dcb51d6a813c7/node_modules/eslint-plugin-import/lib/core/importType.js:126:2822)
at computeRank (/home/jailuser/git/node_modules/.pnpm/eslint-plugin-import@2.32.0_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1___504bceda7a403f00735dcb51d6a813c7/node_modules/eslint-plugin-import/lib/rules/order.js:529:43)(import/order)
packages/cache/src/cache.ts
[error] 1-1: Resolve error: EACCES: permission denied, open '/qFyabQvaVN'
at Object.writeFileSync (node:fs:2409:20)
at l (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:13685)
at createFilesMatcher (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:14437)
at Object.resolve (/home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import@2.32.0_eslint@9.34.0_jiti@2.5.1_/node_modules/eslint-import-resolver-typescript/lib/index.cjs:298:107)
at withResolver (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:180:23)
at fullResolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:201:22)
at relative (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:217:10)
at resolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:233:12)
at resolveImportType (/home/jailuser/git/node_modules/.pnpm/eslint-plugin-import@2.32.0_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1___504bceda7a403f00735dcb51d6a813c7/node_modules/eslint-plugin-import/lib/core/importType.js:126:2822)
at computeRank (/home/jailuser/git/node_modules/.pnpm/eslint-plugin-import@2.32.0_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1___504bceda7a403f00735dcb51d6a813c7/node_modules/eslint-plugin-import/lib/rules/order.js:529:43)(import/order)
[error] 1-1: Resolve error: EACCES: permission denied, open '/qhMtTvcapn'
at Object.writeFileSync (node:fs:2409:20)
at l (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:13685)
at createFilesMatcher (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:14437)
at Object.resolve (/home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import@2.32.0_eslint@9.34.0_jiti@2.5.1_/node_modules/eslint-import-resolver-typescript/lib/index.cjs:298:107)
at withResolver (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:180:23)
at fullResolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:201:22)
at relative (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:217:10)
at resolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:233:12)
at checkSourceValue (/home/jailuser/git/node_modules/.pnpm/eslint-plugin-import@2.32.0_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1___504bceda7a403f00735dcb51d6a813c7/node_modules/eslint-plugin-import/lib/rules/no-useless-path-segments.js:85:53)
at checkSourceValue (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/moduleVisitor.js:32:5)(import/no-useless-path-segments)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: e2e / run
- GitHub Check: build / run
- GitHub Check: test-build-split (ui)
- GitHub Check: test-build-split (docs)
- GitHub Check: test-build-split (api)
- GitHub Check: test-build-split (worker)
- GitHub Check: test-build-split (gateway)
- GitHub Check: test-build-unified
🔇 Additional comments (13)
apps/ui/src/components/models/all-models.tsx (1)
65-66: Import order OK — verify @llmgateway/models resolution & client-safety
- apps/ui/src/components/models/all-models.tsx (import at lines 65–66): import style looks fine.
- apps/ui/package.json: @llmgateway/models is present (script showed ✔ Dependency present).
- Could not locate a package.json named "@llmgateway/models" in the repo — confirm whether that package is published or a workspace package and ensure apps/ui's tsconfig paths / resolver and bundler resolve the alias.
- Verify @llmgateway/models is browser/client-safe (no node:, fs, path, child_process imports or process.env usage) because this file is a client component.
apps/api/src/routes/keys-provider.ts (1)
5-7: Imports reordered to match eslint-plugin-import — LGTM.Grouping external modules first and colocating db/model imports is consistent with resolver rules.
apps/ui/src/components/landing/hero.tsx (2)
10-14: Imports consolidated and grouped correctly.Top-level alias imports placed with the "@/..." group ahead of relative imports; aligns with eslint-plugin-import/order. No duplication left behind.
219-219: Verify Tailwind class "h-10.5".Search returned no evidence this utility exists in the repo; define it in tailwind.config.js or replace with an explicit/arbitrary value.
File: apps/ui/src/components/landing/hero.tsx (around line 219)
- className="h-10.5 rounded-xl px-5"
- className="h-[42px] rounded-xl px-5"
eslint.config.mjs (3)
42-47: Good call enabling no-useless-path-segments.
noUselessIndex: truewill clean up noisyindexsegments.
99-104: Commented rule: decide policy before enablingno-relative-parent-imports.Enabling this without complete path aliases will break imports. If you want this later, pair it with path aliases (tsconfig
paths) and resolver config, then enable behind a short-lived branch flag.
2-2: Plugin import OK — pinned in root package.jsonRoot package.json lists eslint-plugin-import@2.32.0 and eslint-import-resolver-typescript@4.4.4; workspace package.json files do not include them. Confirm root-only devDeps are intentional for monorepo linting, or add the packages to workspace package.jsons if lint runs per-package.
apps/ui/src/app/blog/[slug]/page.tsx (2)
13-14: Type‑only import style LGTM.Consistent with TS best practices; keep this paired with
@typescript-eslint/consistent-type-imports(separateTypeImports=true).
1-2: Remove inlineeslint-disable-next-line import/order; configure ESLint to handle TypeScript +content-collections
- Update eslint.config.mjs: set import/resolver.typescript.project to ['apps//tsconfig.json','packages//tsconfig.json'] and alwaysTryTypes: true; add 'import/order' with groups ['builtin','external','internal','type','parent','sibling','index'] and pathGroups for 'content-collections' (group: 'external', position: 'before') and '@/**' (group: 'internal', position: 'after'); enable 'import/no-duplicates' with considerSeparateImports: true.
- Use a writable ESLint cache in CI to avoid EACCES: run eslint --cache --cache-location .eslintcache (or set TMPDIR="$PWD/.tmp/eslint").
- CI/local lint ran no results because the codeframe formatter is missing; install eslint-formatter-codeframe or run with a supported formatter, then re-run ESLint and paste output to confirm the inline disable can be removed.
Location: apps/ui/src/app/blog/[slug]/page.tsx
apps/ui/src/app/changelog/page.tsx (2)
6-7: Type‑only import separation LGTM.Matches the chosen style and works well with
consistent-type-imports.
1-2: Same: remove per-line disable — configure import/order pathGroups/resolver for content-collectionsFile: apps/ui/src/app/changelog/page.tsx (lines 1–2). Verification blocked: npx eslint returned "The codeframe formatter is no longer part of core ESLint" and produced no lint output. Install the formatter or run eslint with a supported formatter, then re-run the verification script and attach the ESLint output:
npm install -D eslint-formatter-codeframe
TMPDIR="$PWD/.tmp/eslint" npx eslint apps/ui/src/app/changelog/page.tsx -f codeframe --cache --cache-location .eslintcacheapps/ui/src/app/changelog/[slug]/page.tsx (2)
13-14: Type‑only import style LGTM.
1-2: Remove the inline eslint-disable-next-line import/order; configure import/order instead.Delete the inline disable and mirror the project's import/order configuration so imports from "content-collections" are ordered deterministically; run ESLint to confirm no violations.
// eslint-disable-next-line import/order import { allChangelogs } from "content-collections";
| "import/order": [ | ||
| "error", | ||
| { | ||
| groups: [ | ||
| ["builtin"], | ||
| ["external"], | ||
| // Internals | ||
| ["internal", "unknown", "parent", "sibling", "index"], | ||
| // Types | ||
| ["object", "type"], | ||
| ], | ||
| "newlines-between": "always", | ||
| alphabetize: { order: "asc", caseInsensitive: true }, | ||
| warnOnUnassignedImports: true, | ||
| pathGroups: [ | ||
| { | ||
| pattern: "^\\u0000", | ||
| group: "builtin", | ||
| position: "before", | ||
| }, | ||
| { | ||
| pattern: "@/**", | ||
| group: "internal", | ||
| position: "before", | ||
| }, | ||
| { | ||
| pattern: "@llmgateway/**", | ||
| group: "internal", | ||
| position: "before", | ||
| }, | ||
| ], | ||
| pathGroupsExcludedImportTypes: ["builtin", "type"], | ||
| }, | ||
| ], |
There was a problem hiding this comment.
warnOnUnassignedImports will flag import "dotenv/config" introduced in this PR.
With import/order at "error", this will fail CI on side‑effect imports. Either disable that warning or handle via an allowlist.
Apply this change and add an explicit allow for the env preload:
"newlines-between": "always",
alphabetize: { order: "asc", caseInsensitive: true },
- warnOnUnassignedImports: true,
+ warnOnUnassignedImports: false,
pathGroups: [
+ {
+ pattern: "dotenv/config",
+ group: "builtin",
+ position: "before",
+ },
{
pattern: "^\\u0000",
group: "builtin",
position: "before",
},Optionally enforce the “no require/dynamic import” learning:
rules: {
+ "import/no-dynamic-require": "error",
+ "import/no-amd": "error",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "import/order": [ | |
| "error", | |
| { | |
| groups: [ | |
| ["builtin"], | |
| ["external"], | |
| // Internals | |
| ["internal", "unknown", "parent", "sibling", "index"], | |
| // Types | |
| ["object", "type"], | |
| ], | |
| "newlines-between": "always", | |
| alphabetize: { order: "asc", caseInsensitive: true }, | |
| warnOnUnassignedImports: true, | |
| pathGroups: [ | |
| { | |
| pattern: "^\\u0000", | |
| group: "builtin", | |
| position: "before", | |
| }, | |
| { | |
| pattern: "@/**", | |
| group: "internal", | |
| position: "before", | |
| }, | |
| { | |
| pattern: "@llmgateway/**", | |
| group: "internal", | |
| position: "before", | |
| }, | |
| ], | |
| pathGroupsExcludedImportTypes: ["builtin", "type"], | |
| }, | |
| ], | |
| "import/order": [ | |
| "error", | |
| { | |
| groups: [ | |
| ["builtin"], | |
| ["external"], | |
| // Internals | |
| ["internal", "unknown", "parent", "sibling", "index"], | |
| // Types | |
| ["object", "type"], | |
| ], | |
| "newlines-between": "always", | |
| alphabetize: { order: "asc", caseInsensitive: true }, | |
| warnOnUnassignedImports: false, | |
| pathGroups: [ | |
| { | |
| pattern: "dotenv/config", | |
| group: "builtin", | |
| position: "before", | |
| }, | |
| { | |
| pattern: "^\\u0000", | |
| group: "builtin", | |
| position: "before", | |
| }, | |
| { | |
| pattern: "@/**", | |
| group: "internal", | |
| position: "before", | |
| }, | |
| { | |
| pattern: "@llmgateway/**", | |
| group: "internal", | |
| position: "before", | |
| }, | |
| ], | |
| pathGroupsExcludedImportTypes: ["builtin", "type"], | |
| }, | |
| ], |
🤖 Prompt for AI Agents
In eslint.config.mjs around lines 48–81, the import/order option
warnOnUnassignedImports will flag the side‑effect preload import (import
"dotenv/config") added in this PR; either set warnOnUnassignedImports to false
or add an explicit allowlist for the env preload (e.g., add a pathGroups entry
that matches "dotenv/config" and treats it as an internal/builtin/preferred
group so it’s exempted from the unassigned import warning), and optionally add a
separate rule to forbid dynamic require/imports if you want to enforce no
runtime imports.
| @@ -1,6 +1,7 @@ | |||
| import crypto from "crypto"; | |||
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Verify new eslint import resolver config; resolver throws EACCES in analysis.
Static analysis shows eslint-import-resolver-typescript hitting EACCES during resolution, which can fail CI. Likely a misconfigured project glob resolving to / in a mono‑repo.
Run to debug resolver paths:
If it points to root or an invalid path, update flat config:
// eslint.config.mjs (settings snippet)
export default [
{
settings: {
'import/resolver': {
typescript: {
project: ['tsconfig.json', 'packages/*/tsconfig.json'],
alwaysTryTypes: true,
},
},
},
},
];Also ensure import/order and import/no-useless-path-segments are configured with appropriate groups for "type" and monorepo path aliases.
🏁 Script executed:
#!/bin/bash
# Inspect resolver behavior on this file
pnpm exec eslint packages/cache/src/cache.ts --debug 2>&1 | rg -n "import/resolver|typescript|project|tsconfig" -C2Length of output: 4858
Fix eslint-import-resolver-typescript EACCES by narrowing typescript.project globs
ESLint debug shows get-tsconfig failing to write temp files to / (Resolve error: EACCES open '/RzIpQSnmkq' and '/jLdeFbbUqx') and "Multiple projects found" — the resolver's project glob is matching too broadly and causing permission errors.
- Update ESLint resolver settings (e.g., eslint.config.mjs) to explicitly list tsconfig files instead of a broad glob. Example: settings['import/resolver'] = { typescript: { project: ['tsconfig.json','packages/*/tsconfig.json'], alwaysTryTypes: true } }.
- Or switch to a single root tsconfig with references, or restrict the resolver/parser (avoid scanning
/) so get-tsconfig cannot attempt writes to root. - Re-run: pnpm exec eslint packages/cache/src/cache.ts --debug and confirm EACCES is resolved.
🧰 Tools
🪛 ESLint
[error] 1-1: Resolve error: EACCES: permission denied, open '/qFyabQvaVN'
at Object.writeFileSync (node:fs:2409:20)
at l (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:13685)
at createFilesMatcher (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:14437)
at Object.resolve (/home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import@2.32.0_eslint@9.34.0_jiti@2.5.1_/node_modules/eslint-import-resolver-typescript/lib/index.cjs:298:107)
at withResolver (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:180:23)
at fullResolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:201:22)
at relative (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:217:10)
at resolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:233:12)
at resolveImportType (/home/jailuser/git/node_modules/.pnpm/eslint-plugin-import@2.32.0_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1___504bceda7a403f00735dcb51d6a813c7/node_modules/eslint-plugin-import/lib/core/importType.js:126:2822)
at computeRank (/home/jailuser/git/node_modules/.pnpm/eslint-plugin-import@2.32.0_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1___504bceda7a403f00735dcb51d6a813c7/node_modules/eslint-plugin-import/lib/rules/order.js:529:43)
(import/order)
[error] 1-1: Resolve error: EACCES: permission denied, open '/qhMtTvcapn'
at Object.writeFileSync (node:fs:2409:20)
at l (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:13685)
at createFilesMatcher (/home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.cjs:7:14437)
at Object.resolve (/home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import@2.32.0_eslint@9.34.0_jiti@2.5.1_/node_modules/eslint-import-resolver-typescript/lib/index.cjs:298:107)
at withResolver (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:180:23)
at fullResolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:201:22)
at relative (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:217:10)
at resolve (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/resolve.js:233:12)
at checkSourceValue (/home/jailuser/git/node_modules/.pnpm/eslint-plugin-import@2.32.0_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1___504bceda7a403f00735dcb51d6a813c7/node_modules/eslint-plugin-import/lib/rules/no-useless-path-segments.js:85:53)
at checkSourceValue (/home/jailuser/git/node_modules/.pnpm/eslint-module-utils@2.12.1_@typescript-eslint+parser@8.39.1_eslint@9.34.0_jiti@2.5.1__t_0245afd672d291ebf09c60ebb405cf51/node_modules/eslint-module-utils/moduleVisitor.js:32:5)
(import/no-useless-path-segments)
🤖 Prompt for AI Agents
In packages/cache/src/cache.ts around lines 1 to 1, ESLint's typescript resolver
is matching too broadly causing get-tsconfig to try writing temp files to root
(EACCES); fix the ESLint resolver config (e.g., eslint.config.mjs) to replace
the broad project glob with an explicit list of tsconfig paths (for example
['tsconfig.json','packages/*/tsconfig.json']) or point to a single root tsconfig
with references, and set alwaysTryTypes to true; alternatively restrict
parser/resolver scanning so it cannot traverse /; after updating, re-run pnpm
exec eslint packages/cache/src/cache.ts --debug to confirm the EACCES is
resolved.
Add
eslint-import-resolver-typescriptandeslint-plugin-importto enhance TypeScript import management. Configure rules for import order, path handling, and resolver settings ineslint.config.mjs.Summary by CodeRabbit
Style
Chores
Tests
Refactor