Conversation
✅ Deploy Preview for hyprnote-storybook ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for hyprnote ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughThis PR introduces OpenAPI documentation generation with a typed API client, implements a trial billing feature with Stripe integration and RPC checks, refactors desktop onboarding to use a state machine instead of inline navigation, updates authentication middleware to handle case-insensitive bearer tokens, and replaces model-download onboarding with a configuration notice. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Desktop as Desktop<br/>(Login)
participant API as API Server
participant Stripe as Stripe API
participant DB as Supabase<br/>(DB)
participant Auth as Supabase<br/>(Auth)
User->>Desktop: Signs in
Desktop->>Auth: Authenticate with callback
Auth-->>Desktop: access_token + refresh_token
rect rgb(200, 220, 255)
Note over Desktop,DB: Post-Login Trial Check & Initialization
end
Desktop->>API: GET /rpc/can-start-trial
API->>DB: RPC: can_start_trial()
DB->>DB: Check user profile & subscriptions
DB-->>API: canStartTrial: boolean
API-->>Desktop: { canStartTrial }
alt Trial Eligible
Desktop->>API: POST /billing/start-trial
API->>DB: Fetch stripe_customer_id from profiles
alt No Stripe Customer
API->>Stripe: Create customer
Stripe-->>API: customer_id
API->>DB: Update profiles with customer_id
end
API->>Stripe: Create subscription (14-day trial)
Stripe-->>API: subscription created
API-->>Desktop: { started: true }
rect rgb(200, 255, 200)
Note over Desktop: Refresh session & derive Pro entitlement
end
Desktop->>Auth: Refresh session
Auth-->>Desktop: Updated access_token
Desktop->>Desktop: Decode Pro entitlement from token
Desktop->>Desktop: Set local: false (cloud mode)
else Trial Not Eligible
API-->>Desktop: { started: false }
Desktop->>Desktop: Set local: true (local mode)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes
Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (3)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/web/src/env.ts (1)
28-38: Reconsider defaulting VITE_API_URL to the production hostUsing
default("https://api.hyprnote.com")means any environment withoutVITE_API_URLset (including local dev or CI) will silently talk to production, which is risky for data and billing flows. WithemptyStringAsUndefined: true, even an empty var will fall back to prod. TheskipValidation: isCIsetting means CI won't catch a missing var either.Compare this to
apps/desktop/src/env.ts, which correctly defaultsVITE_API_URLto"http://localhost:8787"for safe local development. There is no.env.sampleto guide developers on required setup.If you don't explicitly want that behavior, consider requiring the value instead and setting it per-env in
.envfiles:- VITE_API_URL: z.string().default("https://api.hyprnote.com"), + VITE_API_URL: z.string().min(1),or introduce a dev-only default that doesn't point at production.
🧹 Nitpick comments (13)
Taskfile.yaml (1)
94-98: LGTM!The new
supabase-no-exposetask is correctly implemented. It provides a useful alternative workflow that updates Supabase environment variables without re-exposing the tunnel, complementing the existingsupabase-exposetask.Optional: Extract shared commands to reduce duplication.
The first two commands (
supabase-tunnel-stopandsupabase-env) are duplicated betweensupabase-exposeandsupabase-no-expose. Consider extracting them into a shared task (e.g.,supabase-env-update) to adhere to the DRY principle:+ supabase-env-update: + platforms: [darwin] + cmds: + - task: supabase-tunnel-stop + - task: supabase-env + supabase-expose: platforms: [darwin] cmds: - - task: supabase-tunnel-stop - - task: supabase-env + - task: supabase-env-update - task: supabase-tunnel-start supabase-no-expose: platforms: [darwin] cmds: - - task: supabase-tunnel-stop - - task: supabase-env + - task: supabase-env-updateThis refactoring centralizes the shared workflow and makes it easier to maintain both tasks.
apps/desktop/src/components/settings/account.tsx (1)
226-236: Consider handling loading and error states for trial eligibility.The
canTrialQuery.datacheck doesn't account for loading or error states. Users might briefly see the "Upgrade to Pro" button while the trial eligibility check is pending.+ if (canTrialQuery.isPending) { + return ( + <Button variant="outline" disabled> + <span>Loading...</span> + </Button> + ); + } + if (canTrialQuery.data) { return ( <Buttonapps/desktop/src/components/onboarding/shared.tsx (1)
6-9: Consider usingOnboardingStepIdinstead ofstringfor better type safety.The
stepfield is typed asstring, but it represents anOnboardingStepId. This requires a cast inmachine.ts(line 107 in onboarding/index.tsx). Using the specific type would provide compile-time validation.+import type { OnboardingStepId } from "./config"; + export type OnboardingNext = (params?: { local?: boolean; - step?: string; + step?: OnboardingStepId; }) => void;apps/desktop/src/components/onboarding/login.tsx (2)
21-55: Missing cleanup for async operation may cause issues on unmount.The async IIFE can complete after the component unmounts, potentially calling
onNexton an unmounted component. Consider using an abort pattern:useEffect(() => { if (auth?.session && !trialStarted.current) { trialStarted.current = true; + let cancelled = false; const client = createClient( createConfig({ baseUrl: env.VITE_API_URL, headers: { Authorization: `Bearer ${auth.session.access_token}` }, }), ); (async () => { try { const { data } = await getRpcCanStartTrial({ client }); if (data?.canStartTrial) { await postBillingStartTrial({ client, query: { interval: "monthly" }, }); } const newSession = await auth.refreshSession(); const isPro = newSession ? getEntitlementsFromToken(newSession.access_token).includes( "hyprnote_pro", ) : false; + if (!cancelled) onNext({ local: !isPro }); - onNext({ local: !isPro }); } catch (e) { console.error("Failed to process login:", e); + if (!cancelled) onNext({ local: true }); - onNext({ local: true }); } })(); + + return () => { + cancelled = true; + }; } }, [auth?.session, auth?.refreshSession, onNext]);Also note: users see "Waiting for sign in..." even after they're logged in while the trial logic runs. Consider adding a loading/processing state for better UX.
77-83: Consider validating callback URL before submission.The Submit button calls
handleAuthCallbackwithout validating thatcallbackUrlis non-empty or contains expected tokens. This could lead to silent failures or confusing error states.<Button - onClick={() => auth?.handleAuthCallback(callbackUrl)} + onClick={() => callbackUrl.trim() && auth?.handleAuthCallback(callbackUrl)} + disabled={!callbackUrl.trim()} className="w-full" > Submit </Button>apps/api/src/env.ts (1)
32-32: Consider the tradeoffs of skipping validation in CI.While skipping environment validation in CI is a common pattern to allow for partial configurations during build/test, it could mask missing or misconfigured environment variables that would fail in production.
Consider whether CI jobs truly need to skip validation, or if test-specific environment files could be provided instead.
apps/web/content/docs/developers/8.api.mdx (1)
7-9: Consider adding a bit more guidance than a one-line placeholderThe “Public API is coming soon.” placeholder is fine short‑term, but given the page title/summary, you might want to add a brief note about current status (e.g., private/internal only) or link to any internal OpenAPI docs so developers aren’t left at a dead end.
apps/api/package.json (1)
6-11: Validate openapi script env behavior and @hypr/api-client dependency scopeThe
openapiscript and OpenAPI tooling wiring look reasonable, but two things are worth double‑checking:
CI=truetriggersskipValidationin your env setup; make suresrc/scripts/generate-openapi.tsdoesn’t rely on validated env vars (e.g., secrets) or, if it does, that they’re documented separately.- If
@hypr/api-clientis only used by the generator and/or for types, consider moving it todevDependenciesto keep the API service’s runtime dependency set smaller.[Suggest adjusting only if these assumptions hold.]
Also applies to: 13-27, 30-34
apps/api/src/middleware/load-test-auth.ts (1)
8-15: Case-insensitive Bearer handling improves load-test override robustnessParsing the
Authorizationheader with/^bearer /iis a nice improvement—this will accept any casing ofBearerwhile preserving existing behavior. If you expect headers with irregular spacing, you could further relax it to/^bearer\s+/i, but that’s purely optional for a load-test‑only path.apps/api/src/scripts/generate-openapi.ts (2)
13-14: Consider consistent path resolution.Line 13-14 uses
new URL()withimport.meta.urlfor output path, but line 19 uses a hardcoded relative string"./openapi.gen.json". Line 20 also uses a hardcoded relative path.For consistency and reliability, consider using the same URL-based approach for all paths:
+ const inputPath = new URL("../../openapi.gen.json", import.meta.url).pathname; + const outputPath = new URL("../../packages/api-client/src/generated", import.meta.url).pathname; + try { await createClient({ - input: "./openapi.gen.json", - output: "../../packages/api-client/src/generated", + input: inputPath, + output: outputPath, parser: {This ensures paths work correctly regardless of the working directory when the script is executed.
Also applies to: 19-20
30-33: Error handling terminates abruptly.Line 32 uses
process.exit(1)which immediately terminates the process without cleanup. While acceptable for build scripts, consider whether any cleanup is needed.This is appropriate for a build script, but if you need to ensure cleanup (closing connections, flushing logs, etc.), consider:
} catch (error) { console.error("Failed to generate OpenAPI client:"); console.error(error); // Perform any necessary cleanup here process.exit(1); }Alternatively, let the error propagate naturally and let the runtime handle it if no cleanup is needed.
apps/api/openapi.gen.json (1)
1-466: Verify security design is intentional.The static analysis tools flag missing global security rules and suggest array constraints. While this is a generated file, consider:
Security design: Some endpoints (health, billing, rpc) have no security requirements while others use Bearer auth. Verify this mixed approach aligns with your security model.
Array validation: The
messagesarray (lines 139-161) and similar arrays lackmaxItemsconstraints. Consider adding limits in the source schemas to prevent abuse.Do you want me to help identify where in the source code these constraints should be added?
apps/web/src/functions/billing.ts (1)
223-288: Consider extracting shared customer logic.The customer retrieval/creation logic (lines 226-263) is duplicated from
createCheckoutSession. Consider extracting this into a shared helper function to reduce duplication and improve maintainability.Apply this refactor:
// Extract shared logic const getOrCreateStripeCustomer = async ( supabase: SupabaseClient, user: AuthUser, stripe: ReturnType<typeof getStripeClient> ) => { let stripeCustomerId = await getStripeCustomerIdForUser(supabase, user); if (!stripeCustomerId) { const newCustomer = await stripe.customers.create({ email: user.email, metadata: { userId: user.id }, }); await Promise.all([ supabase.auth.updateUser({ data: { stripe_customer_id: newCustomer.id }, }), supabase .from("profiles") .update({ stripe_customer_id: newCustomer.id }) .eq("id", user.id), ]); stripeCustomerId = newCustomer.id; } return stripeCustomerId; }; // Then use in both functions: const stripeCustomerId = await getOrCreateStripeCustomer( supabase, { id: user.id, user_metadata: user.user_metadata }, stripe );
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (14)
packages/api-client/src/generated/client.gen.tsis excluded by!**/generated/**,!**/generated/**,!**/*.gen.tspackages/api-client/src/generated/client/client.tsis excluded by!**/generated/**,!**/generated/**packages/api-client/src/generated/client/index.tsis excluded by!**/generated/**,!**/generated/**packages/api-client/src/generated/client/types.tsis excluded by!**/generated/**,!**/generated/**packages/api-client/src/generated/client/utils.tsis excluded by!**/generated/**,!**/generated/**packages/api-client/src/generated/core/auth.tsis excluded by!**/generated/**,!**/generated/**packages/api-client/src/generated/core/bodySerializer.tsis excluded by!**/generated/**,!**/generated/**packages/api-client/src/generated/core/params.tsis excluded by!**/generated/**,!**/generated/**packages/api-client/src/generated/core/pathSerializer.tsis excluded by!**/generated/**,!**/generated/**packages/api-client/src/generated/core/types.tsis excluded by!**/generated/**,!**/generated/**packages/api-client/src/generated/index.tsis excluded by!**/generated/**,!**/generated/**packages/api-client/src/generated/sdk.gen.tsis excluded by!**/generated/**,!**/generated/**,!**/*.gen.tspackages/api-client/src/generated/types.gen.tsis excluded by!**/generated/**,!**/generated/**,!**/*.gen.tspnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (44)
.vscode/settings.json(1 hunks)Taskfile.yaml(1 hunks)apps/api/openapi.gen.json(1 hunks)apps/api/package.json(2 hunks)apps/api/src/env.ts(2 hunks)apps/api/src/hono-bindings.ts(2 hunks)apps/api/src/index.ts(2 hunks)apps/api/src/middleware/load-test-auth.ts(1 hunks)apps/api/src/middleware/supabase.ts(2 hunks)apps/api/src/openapi.ts(1 hunks)apps/api/src/routes/billing.ts(1 hunks)apps/api/src/routes/constants.ts(1 hunks)apps/api/src/routes/health.ts(1 hunks)apps/api/src/routes/index.ts(2 hunks)apps/api/src/routes/llm.ts(1 hunks)apps/api/src/routes/rpc.ts(1 hunks)apps/api/src/routes/stt.ts(2 hunks)apps/api/src/routes/webhook.ts(1 hunks)apps/api/src/scripts/generate-openapi.ts(1 hunks)apps/desktop/package.json(1 hunks)apps/desktop/src/auth.tsx(4 hunks)apps/desktop/src/billing.tsx(2 hunks)apps/desktop/src/components/onboarding/config.tsx(2 hunks)apps/desktop/src/components/onboarding/configure-notice.tsx(1 hunks)apps/desktop/src/components/onboarding/login.tsx(2 hunks)apps/desktop/src/components/onboarding/machine.ts(1 hunks)apps/desktop/src/components/onboarding/model.tsx(0 hunks)apps/desktop/src/components/onboarding/shared.tsx(1 hunks)apps/desktop/src/components/settings/account.tsx(2 hunks)apps/desktop/src/routes/app/onboarding/index.tsx(2 hunks)apps/desktop/src/routes/app/settings/_layout.tsx(3 hunks)apps/desktop/src/utils/index.ts(1 hunks)apps/web/content/docs/developers/8.api.mdx(1 hunks)apps/web/package.json(1 hunks)apps/web/src/components/openapi-docs.tsx(2 hunks)apps/web/src/env.ts(1 hunks)apps/web/src/functions/billing.ts(2 hunks)apps/web/src/middleware/supabase.ts(3 hunks)apps/web/src/routes/_view/app/account.tsx(5 hunks)apps/web/src/routes/_view/callback/auth.tsx(1 hunks)packages/api-client/package.json(1 hunks)packages/api-client/tsconfig.json(1 hunks)plugins/webhook/src/lib.rs(1 hunks)supabase/migrations/20250101000004_can_start_trial.sql(1 hunks)
💤 Files with no reviewable changes (1)
- apps/desktop/src/components/onboarding/model.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, usecn(import from@hypr/utils). It is similar toclsx. Always pass an array and split by logical grouping.
Usemotion/reactinstead offramer-motion.
Files:
apps/web/src/components/openapi-docs.tsxapps/api/src/routes/llm.tsapps/desktop/src/components/onboarding/shared.tsxapps/api/src/middleware/supabase.tsapps/api/src/routes/webhook.tsapps/api/src/routes/billing.tsapps/web/src/functions/billing.tsapps/desktop/src/utils/index.tsapps/api/src/routes/rpc.tsapps/api/src/scripts/generate-openapi.tsapps/api/src/hono-bindings.tsapps/desktop/src/components/onboarding/configure-notice.tsxapps/api/src/routes/health.tsapps/api/src/routes/stt.tsapps/desktop/src/components/onboarding/machine.tsapps/api/src/routes/index.tsapps/web/src/routes/_view/app/account.tsxapps/web/src/routes/_view/callback/auth.tsxapps/desktop/src/routes/app/onboarding/index.tsxapps/api/src/middleware/load-test-auth.tsapps/desktop/src/components/onboarding/config.tsxapps/desktop/src/components/onboarding/login.tsxapps/api/src/env.tsapps/desktop/src/components/settings/account.tsxapps/api/src/routes/constants.tsapps/api/src/index.tsapps/api/src/openapi.tsapps/web/src/env.tsapps/desktop/src/auth.tsxapps/desktop/src/routes/app/settings/_layout.tsxapps/desktop/src/billing.tsxapps/web/src/middleware/supabase.ts
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces
Files:
apps/api/src/routes/llm.tsapps/api/src/middleware/supabase.tsapps/api/src/routes/webhook.tsapps/api/src/routes/billing.tsapps/web/src/functions/billing.tsapps/desktop/src/utils/index.tsapps/api/src/routes/rpc.tsapps/api/src/scripts/generate-openapi.tsapps/api/src/hono-bindings.tsapps/api/src/routes/health.tsapps/api/src/routes/stt.tsapps/desktop/src/components/onboarding/machine.tsapps/api/src/routes/index.tsapps/api/src/middleware/load-test-auth.tsapps/api/src/env.tsapps/api/src/routes/constants.tsapps/api/src/index.tsapps/api/src/openapi.tsapps/web/src/env.tsapps/web/src/middleware/supabase.ts
plugins/*/src/lib.rs
📄 CodeRabbit inference engine (plugins/AGENTS.md)
After updating commands in
plugins/<NAME>/src/lib.rs, runcodegen, updateplugins/<NAME>/permissions/default.toml, andapps/desktop/src-tauri/capabilities/default.json
Files:
plugins/webhook/src/lib.rs
🧠 Learnings (4)
📚 Learning: 2025-11-24T16:32:13.593Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: packages/nango/.cursor/rules/nango.mdc:0-0
Timestamp: 2025-11-24T16:32:13.593Z
Learning: Applies to packages/nango/**/*.ts : API endpoints must be correctly implemented according to actual API documentation
Applied to files:
apps/web/src/components/openapi-docs.tsxapps/api/src/index.ts
📚 Learning: 2025-11-24T16:32:13.593Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: packages/nango/.cursor/rules/nango.mdc:0-0
Timestamp: 2025-11-24T16:32:13.593Z
Learning: Applies to packages/nango/**/models.ts : Do NOT edit the models.ts file - it is automatically generated at compilation time
Applied to files:
packages/api-client/tsconfig.json.vscode/settings.json
📚 Learning: 2025-11-27T11:40:22.782Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: plugins/AGENTS.md:0-0
Timestamp: 2025-11-27T11:40:22.782Z
Learning: Applies to plugins/*/src/lib.rs : After updating commands in `plugins/<NAME>/src/lib.rs`, run `codegen`, update `plugins/<NAME>/permissions/default.toml`, and `apps/desktop/src-tauri/capabilities/default.json`
Applied to files:
plugins/webhook/src/lib.rs
📚 Learning: 2025-11-24T16:32:13.593Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: packages/nango/.cursor/rules/nango.mdc:0-0
Timestamp: 2025-11-24T16:32:13.593Z
Learning: Applies to packages/nango/**/*.ts : Do NOT set Authorization header - Nango handles this automatically
Applied to files:
apps/api/src/middleware/load-test-auth.ts
🧬 Code graph analysis (19)
apps/api/src/routes/llm.ts (2)
apps/api/src/routes/constants.ts (1)
API_TAGS(1-5)apps/api/src/routes/index.ts (1)
API_TAGS(11-11)
apps/api/src/routes/webhook.ts (1)
apps/api/src/routes/constants.ts (1)
API_TAGS(1-5)
apps/web/src/functions/billing.ts (4)
apps/web/src/functions/supabase.ts (1)
getSupabaseServerClient(20-36)apps/web/src/env.ts (1)
env(7-43)apps/api/src/env.ts (1)
env(4-33)apps/web/src/functions/stripe.ts (1)
getStripeClient(6-10)
apps/api/src/routes/rpc.ts (3)
apps/api/src/hono-bindings.ts (1)
AppBindings(7-17)apps/api/src/routes/constants.ts (1)
API_TAGS(1-5)apps/web/src/middleware/supabase.ts (1)
supabaseAuthMiddleware(6-39)
apps/api/src/scripts/generate-openapi.ts (3)
apps/api/src/routes/index.ts (2)
routes(13-13)API_TAGS(11-11)apps/api/src/openapi.ts (1)
openAPIDocumentation(3-28)apps/api/src/routes/constants.ts (1)
API_TAGS(1-5)
apps/desktop/src/components/onboarding/configure-notice.tsx (2)
apps/desktop/src/components/onboarding/shared.tsx (1)
OnboardingNext(6-9)extensions/shared/types/hypr-extension.d.ts (1)
Button(159-159)
apps/api/src/routes/health.ts (1)
apps/api/src/routes/constants.ts (1)
API_TAGS(1-5)
apps/api/src/routes/stt.ts (2)
apps/api/src/routes/constants.ts (1)
API_TAGS(1-5)apps/api/src/routes/index.ts (1)
API_TAGS(11-11)
apps/desktop/src/components/onboarding/machine.ts (1)
apps/desktop/src/components/onboarding/config.tsx (3)
OnboardingStepId(13-17)OnboardingContext(19-27)STEP_CONFIGS(35-56)
apps/api/src/routes/index.ts (3)
apps/api/src/routes/billing.ts (1)
billing(20-20)apps/api/src/routes/llm.ts (1)
llm(36-36)apps/api/src/routes/rpc.ts (1)
rpc(14-14)
apps/web/src/routes/_view/app/account.tsx (1)
apps/web/src/functions/billing.ts (2)
canStartTrial(196-221)createTrialCheckoutSession(223-288)
apps/desktop/src/routes/app/onboarding/index.tsx (3)
apps/desktop/src/components/onboarding/config.tsx (3)
OnboardingContext(19-27)OnboardingStepId(13-17)STEP_CONFIGS(35-56)apps/desktop/src/components/onboarding/machine.ts (2)
createOnboardingLogic(28-50)OnboardingState(9-13)apps/desktop/src/components/onboarding/shared.tsx (1)
OnboardingNext(6-9)
apps/desktop/src/components/onboarding/config.tsx (1)
apps/desktop/src/components/onboarding/configure-notice.tsx (1)
ConfigureNotice(9-45)
apps/desktop/src/components/onboarding/login.tsx (4)
apps/desktop/src/components/onboarding/shared.tsx (2)
OnboardingNext(6-9)OnboardingContainer(24-51)apps/desktop/src/auth.tsx (1)
useAuth(299-307)apps/desktop/src/billing.tsx (1)
getEntitlementsFromToken(14-21)extensions/shared/types/hypr-extension.d.ts (2)
Input(322-322)Button(159-159)
apps/desktop/src/components/settings/account.tsx (3)
apps/desktop/src/auth.tsx (1)
useAuth(299-307)apps/desktop/src/billing.tsx (1)
useBillingAccess(66-74)apps/desktop/src/env.ts (1)
env(4-17)
apps/api/src/index.ts (2)
apps/api/src/routes/index.ts (1)
routes(13-13)apps/api/src/openapi.ts (1)
openAPIDocumentation(3-28)
apps/api/src/openapi.ts (1)
apps/api/src/routes/constants.ts (1)
API_TAGS(1-5)
apps/desktop/src/auth.tsx (1)
packages/store/src/schema-external.ts (1)
Session(169-169)
apps/web/src/middleware/supabase.ts (2)
apps/api/src/middleware/supabase.ts (1)
supabaseAuthMiddleware(7-33)apps/web/src/env.ts (1)
env(7-43)
🪛 Checkov (3.2.334)
apps/api/openapi.gen.json
[high] 1-465: Ensure that the global security field has rules defined
(CKV_OPENAPI_4)
[high] 1-465: Ensure that security operations is not empty.
(CKV_OPENAPI_5)
[medium] 139-161: Ensure that arrays have a maximum number of items
(CKV_OPENAPI_21)
⏰ 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). (9)
- GitHub Check: Redirect rules - hyprnote
- GitHub Check: Header rules - hyprnote
- GitHub Check: Pages changed - hyprnote
- GitHub Check: desktop_ci (linux, depot-ubuntu-22.04-8)
- GitHub Check: desktop_ci (linux, depot-ubuntu-24.04-8)
- GitHub Check: desktop_ci (macos, depot-macos-14)
- GitHub Check: tests
- GitHub Check: ci
- GitHub Check: fmt
🔇 Additional comments (48)
apps/desktop/src/routes/app/settings/_layout.tsx (1)
7-14: New “Data Import” tab wiring looks consistent and safe while disabledThe
Importicon import,"data-import"addition toTAB_KEYS, and itsTAB_CONFIGentry are all consistent with the existing pattern. Because it’s markeddisabled: true, it won’t appear inenabledTabsor the sidebar, but remains a validTabKeyand search param for future use. No issues from a routing, typing, or UX perspective.Also applies to: 23-34, 73-78
apps/web/src/routes/_view/callback/auth.tsx (1)
87-100: Shorter desktop redirect delay looks goodChanging the timeout to 200ms should make the desktop deeplink feel more responsive, and it doesn’t introduce any new correctness issues given the current flow and dependencies.
apps/desktop/src/billing.tsx (2)
14-21: Clean refactoring that improves code organization.The extracted helper function makes the code more maintainable and reusable. The error handling correctly returns a safe default on JWT decode failure.
40-40: LGTM!Correct usage of the helper function. The early return check for missing tokens (lines 37-39) ensures the helper receives a valid token.
apps/desktop/src/utils/index.ts (1)
8-17: LGTM!The
getSchemefunction is well-structured with a clear mapping and sensible default fallback. Minor note: the localidvariable shadows the exportedidfunction in this module, but since they're in different scopes this doesn't cause issues.apps/desktop/src/auth.tsx (5)
23-23: LGTM!Clean refactor to use the centralized
getSchemeutility instead of inline implementation.
25-68: LGTM!Good defensive programming with iframe context detection and conditional Tauri storage creation. The
isLocalAuthServerhelper properly handles URL parsing errors, andclearAuthStorageappropriately silences errors during cleanup operations.
70-88: LGTM!The conditional Supabase client creation correctly guards against iframe contexts where Tauri APIs are unavailable. The null-check chain ensures all required configuration is present.
95-95: LGTM!The updated return type
Promise<Session | null>is more informative for callers who need to act on the refreshed session.
244-258: LGTM!The
refreshSessionimplementation correctly returns the session when available andnullon failure, aligning with the updated type signature.apps/desktop/src/components/onboarding/config.tsx (1)
13-17: LGTM!The step configuration update correctly introduces
configure-noticeas a local-only step, with propershouldShowpredicate that displays this step when the user is in local mode.Also applies to: 46-50
apps/desktop/src/components/settings/account.tsx (2)
183-203: Clarify the purpose of the artificial delay.The 3-second
setTimeoutdelay (line 198) before mutation completion is unclear. If this is waiting for backend state propagation (e.g., Stripe webhook processing), consider:
- Adding a comment explaining the rationale
- Polling for the actual state change instead of a fixed delay
- Handling cases where 3 seconds isn't sufficient
mutationFn: async () => { const headers = auth?.getHeaders(); if (!headers) { throw new Error("Not authenticated"); } const client = createClient({ baseUrl: env.VITE_API_URL, headers }); const { error } = await postBillingStartTrial({ client, query: { interval: "monthly" }, }); if (error) { throw error; } + // Wait for Stripe webhook to update subscription status await new Promise((resolve) => setTimeout(resolve, 3000)); },
165-181: LGTM!Good use of
useQuerywith proper query key structure including user ID for cache isolation. Theenabledflag correctly prevents queries when unauthenticated or already Pro.apps/desktop/src/components/onboarding/configure-notice.tsx (1)
9-44: LGTM!The
ConfigureNoticecomponent is well-structured with clear navigation options. The button actions correctly use theonNextcallback with appropriate parameters for both the sign-up redirect and the continue flow.apps/desktop/src/components/onboarding/machine.ts (2)
19-26: Verify behavior when current step is not in visible steps.If
currentStepis not found invisibleSteps,findIndexreturns-1, andvisibleSteps[-1 + 1]returnsvisibleSteps[0], effectively restarting from the first visible step. Verify this is the intended fallback behavior.
28-49: LGTM!The state machine implementation using XState's
fromTransitionis clean and handles bothGO_TO(explicit navigation) andNEXT(sequential progression) events correctly. The context merging withlocaloverride is well-designed.apps/desktop/src/routes/app/onboarding/index.tsx (4)
36-41: LGTM!The
finishOnboardingfunction correctly sequences the window operations—showing the main window before destroying the onboarding window ensures a smooth transition.
85-100: LGTM!The effect correctly uses a ref to track previous state and only triggers navigation when
steporlocalactually changes. The early return onstate.doneprevents unnecessary navigation after completion.
102-115: LGTM!The
goNextcallback correctly dispatchesGO_TOfor explicit step navigation andNEXTfor sequential progression, aligning with the state machine's event handling.
81-83: TheuseSelectorusage withfromTransitionis correct; no changes needed.XState v5's
fromTransitioncreates actor snapshots with acontextproperty that holds the reducer's return value. The snapshot structure is{ status, output, error, ... context: TContext }, so accessings.contextin the selector is the correct pattern. This is confirmed by the existing usage inchat.tsline 50 wheresnapshot.contextis accessed in the same way.apps/desktop/src/components/onboarding/login.tsx (2)
57-62: LGTM!The ref guard pattern correctly ensures sign-in is only initiated once on mount.
96-126: LGTM!The waiting UI provides clear options for troubleshooting sign-in issues and correctly uses
OnboardingContainerfor consistent styling.plugins/webhook/src/lib.rs (1)
64-64: LGTM! Filename aligns with generated file conventions.The change to
openapi.gen.jsonis consistent with the naming pattern for generated files used throughout the PR..vscode/settings.json (1)
8-9: LGTM! Appropriate exclusion for generated code.Excluding the
generated/directory from search results improves developer experience by hiding auto-generated files.packages/api-client/tsconfig.json (1)
1-13: LGTM! Well-configured for a generated API client.The TypeScript configuration appropriately enables strict mode, declaration generation, and uses modern ESNext with bundler resolution.
supabase/migrations/20250101000004_can_start_trial.sql (2)
1-36: Well-implemented security-definer function with proper privilege control.The function correctly uses
SECURITY DEFINERwithsearch_path = ''to prevent schema injection attacks, and properly restricts access to authenticated users only.
24-34: The trial_start extraction could be clearer.The
stripe.subscriptions.trial_startcolumn stores JSONB data containing Unix timestamps from the Stripe API (numeric values). The expression(trial_start #>> '{}')::bigintworks correctly—#>>'{}extracts the JSON scalar as text, and::bigintconverts it to a number. However, this operator choice is unconventional for scalar values. Consider using(trial_start::text)::bigintor a direct cast for improved readability.apps/desktop/package.json (1)
18-18: LGTM! Standard workspace dependency addition.The new
@hypr/api-clientdependency enables the desktop app to consume the generated API client for billing and trial functionality.apps/web/package.json (1)
15-15: LGTM! Standard workspace dependency addition.The new
@hypr/api-clientdependency enables the web app to consume the generated API client for billing and trial functionality.apps/api/src/middleware/supabase.ts (2)
14-14: Good improvement: case-insensitive Bearer token parsing.Using a case-insensitive regex makes the middleware more robust against client implementations that may send "bearer", "Bearer", or "BEARER".
30-30: Good improvement: context-sharing the Supabase client.Storing the authenticated Supabase client in the request context allows downstream routes to reuse it without recreating clients, improving efficiency and maintaining consistent authentication context.
apps/web/src/components/openapi-docs.tsx (1)
71-85: Update of OpenAPI spec path is consistent and safeSwitching both the fetch URL and the “View raw OpenAPI spec” link to
/openapi.gen.jsonkeeps the UI in sync with the new generated spec location; no issues from this change as long as the API serves that path in all environments.Also applies to: 133-140
apps/api/src/routes/index.ts (1)
4-9: Billing and RPC routers are correctly mountedImporting
billingandrpcand routing them under/billingand/rpcmatches the existing Hono composition pattern in this file; the wiring looks correct.Also applies to: 15-20
packages/api-client/package.json (1)
1-7: API client package exports align with generated layoutThe new
@hypr/api-clientmanifest cleanly exposes the generated root and/cliententrypoints. Just ensuregenerate-openapi.tswrites tosrc/generated/index.tsandsrc/generated/client/index.tsas declared here; you can add fields liketype,version, andlicenselater if/when you plan to publish outside the monorepo.apps/api/src/routes/llm.ts (1)
39-47: /completions marked as private matches intent to keep LLM route internalSwitching the tag to
API_TAGS.PRIVATE_SKIP_OPENAPIand simplifying the 200/401 descriptions makes sense if this endpoint is meant to stay off the public OpenAPI surface while remaining functional. Just confirm your OpenAPI generation pipeline actually filters outPRIVATE_SKIP_OPENAPI‑tagged routes so this doesn’t accidentally leak into the public spec.apps/api/src/hono-bindings.ts (1)
2-2: LGTM!The addition of the
SupabaseClienttype toAppBindings.Variablesis correct and enables type-safe access to the Supabase client in downstream routes via the Hono context.Also applies to: 14-14
apps/api/src/index.ts (1)
21-22: LGTM!The refactoring centralizes OpenAPI documentation configuration, making it easier to maintain. The endpoint path change from "/openapi.json" to "/openapi.gen.json" correctly reflects that the spec is now generated, and all references are consistently updated.
Also applies to: 72-73, 81-81
apps/api/src/routes/webhook.ts (1)
22-22: LGTM!The changes simplify OpenAPI metadata by using the new
PRIVATE_SKIP_OPENAPItag and generic descriptions. This aligns with the centralized OpenAPI documentation approach while preserving runtime behavior.Also applies to: 25-25, 32-33
apps/web/src/routes/_view/app/account.tsx (1)
109-119: Verify boolean check logic for trial eligibility.Line 109 checks
if (canTrialQuery.data)to determine trial eligibility. IfcanStartTrial()legitimately returnsfalse(user not eligible), this condition would correctly hide the button. However, it's worth confirming this is the intended behavior and not confusingfalsewith loading/error states.Verify that
canStartTrial()inapps/web/src/functions/billing.tsreturns:
truewhen eligiblefalsewhen not eligible or on errorIf the function can return
undefined, the check should be:if (canTrialQuery.data === true) {apps/api/src/routes/health.ts (1)
18-18: LGTM!The changes update OpenAPI metadata to use the new
PRIVATE_SKIP_OPENAPItag, aligning with the broader API documentation restructuring. Runtime behavior is unchanged.Also applies to: 21-21
apps/api/src/routes/constants.ts (1)
1-5: LGTM!The refactored API_TAGS constant provides clearer semantics with
PRIVATE_SKIP_OPENAPIfor endpoints excluded from OpenAPI docs andPRIVATEfor documented internal endpoints. The type narrowing withas constis appropriate.apps/api/src/openapi.ts (1)
1-28: LGTM!The centralized OpenAPI documentation structure is well-organized. Note that
PRIVATE_SKIP_OPENAPIis correctly excluded from the tags array since those endpoints should not appear in the generated documentation.apps/api/src/routes/stt.ts (2)
44-52: LGTM!The tag change to
PRIVATE_SKIP_OPENAPIappropriately excludes this WebSocket endpoint from OpenAPI documentation, and the simplified response descriptions are consistent with this intent.
62-77: LGTM!Consistent with the
/listenendpoint changes, the tag update and simplified descriptions appropriately handle this private STT endpoint while maintaining the response schema for type safety.apps/web/src/functions/billing.ts (2)
196-221: LGTM!The
canStartTrialfunction properly validates the session, handles errors gracefully by logging and returningfalse, and uses nullish coalescing to ensure a boolean result.
265-285: LGTM! Trial configuration is correct.The Stripe checkout session correctly implements a 14-day trial with:
payment_method_collection: "if_required"(allows trial start without payment)missing_payment_method: "cancel"(safe default)- Monthly subscription only (appears intentional to simplify trial offering)
apps/web/src/middleware/supabase.ts (2)
8-19: LGTM!The case-insensitive token extraction using
/^bearer /iis more robust than the previous approach and properly handles various capitalizations.
21-37: LGTM!The middleware correctly:
- Creates a per-request Supabase client with the Authorization header in global config
- Strips "bearer " prefix and re-adds "Bearer " for proper header format
- Passes both the client and user in context for downstream usage
This pattern aligns with the API middleware implementation in
apps/api/src/middleware/supabase.ts.
No description provided.