Skip to content

Conversation

@chuckcarpenter
Copy link
Member

@chuckcarpenter chuckcarpenter commented Dec 29, 2025

image image

Summary

  • Replace AWS Cognito with Supabase for authentication using email OTP (6-digit code) flow
  • Simplify auth UX: single login flow handles both new and existing users automatically
  • Add Supabase database schema with Row Level Security for user data isolation

Changes

  • Auth: New supabase service and authenticator with OTP methods (signInWithOtp, verifyOtp)
  • Data: Replace JSONAPI remote source with SupabaseSource for Orbit.js sync
  • UI: Simplified 2-step login (email → code verification), removed password-related flows
  • Schema: supabase-schema.sql with palettes/colors tables, RLS policies, and migration helpers
  • Tests: Updated acceptance tests for OTP flow (75 tests passing)

Breaking Changes

Requires environment variables:

SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=your-anon-key

Future Enhancements

  • Real-time sync integration (foundation is in place)
  • Logout test coverage

Summary by CodeRabbit

  • New Features

    • Passwordless OTP auth via Supabase, Supabase-backed remote data source with realtime sync, and a new Supabase service & config.
  • Removed Features

    • Legacy Cognito integrations and their password/confirmation/reset components removed.
  • UI Changes

    • Login/register/forgot flows converted to OTP-first; profile view simplified to always show "Signed in as...".
  • Documentation

    • Added Supabase migration guide and full Supabase database schema.
  • Tests

    • Acceptance tests updated for Supabase OTP flows.
  • Chores

    • Updated config and ignore lists; replaced Cognito deps with Supabase and added dotenv.

✏️ Tip: You can customize this high-level summary in your review settings.

- Replace Cognito with Supabase for auth (email OTP flow)
- Add new supabase service with OTP methods (signInWithOtp, verifyOtp)
- Add supabase authenticator for ember-simple-auth
- Replace JSONAPI remote source with Supabase data source
- Simplify login flow: single 2-step OTP process for new/existing users
- Remove password-related flows (forgot-password, register-confirm)
- Update tests for new OTP authentication flow
- Add migration documentation

BREAKING: Requires SUPABASE_URL and SUPABASE_ANON_KEY env vars
- Define palettes and colors tables with proper constraints
- Add Row Level Security policies for user data isolation
- Include indexes for query performance
- Add helper functions for data migration from Orbit.js
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 29, 2025

📝 Walkthrough

Walkthrough

Replaces AWS Cognito/JSON:API with Supabase: adds OTP-based Supabase authentication, a Supabase-backed Orbit remote source with realtime sync for palettes/colors, removes Cognito services/components, updates environment/config, dependencies, tests, and adds migration docs and a SQL schema.

Changes

Cohort / File(s) Summary
Build & Ignore / Dependencies
\.prettierignore, \.stylelintignore, eslint\.config\.mjs, package\.json
Added /electron-app/ember-test/ to ignore lists; swapped aws4fetch/ember-cognito for @supabase/supabase-js and added dotenv.
Supabase Service & Authenticator
app/services/supabase\.ts, app/authenticators/supabase\.ts
New SupabaseService (client init, auth/session helpers, OTP API, onAuthStateChange) and SupabaseAuthenticator (OTP authenticate/restore/invalidate).
Cognito Removal
app/services/cognito\.js, app/authenticators/cognito\.js
Deleted Cognito service augmentation and custom Cognito authenticator (session/refresh/AWS credential logic removed).
Session & Registry
app/services/session\.ts, app/services/supabase\.ts
SessionService now uses SupabaseAuthData generic; Ember service registry augmented with supabase: SupabaseService.
Login / Register / Forgot UI & Templates
app/components/login\.gts, app/components/register\.gts, app/components/forgot-password\.gts, app/components/register-confirm\.gts, app/templates/settings/cloud/*
Added two-step OTP login (send/resend/verify); simplified register to onboarding; removed forgot-password and register-confirm components/templates; updated profile template args and route targets.
Remote Data Source Migration
app/data-sources/remote\.js, app/data-sources/remote\.ts
Removed JSONAPI/AWS-signed remote source; added SupabaseSource implementing CRUD, relationship ops, mapping between Orbit and Supabase, realtime Postgres subscriptions, and a create(...) factory.
Data Service
app/services/data\.ts
Reworked to use SupabaseSource: replaced JSON:API interactions with Supabase-specific queryPalettes and sequential uploads; simplified relationship handling.
Routes & Route Logic
app/router\.ts, app/routes/settings/cloud/index\.ts, app/routes/settings/cloud/profile\.ts
Removed forgot-password route and nested register children; profile route builds ProfileModel from session data and removed Cognito injection.
Environment Config
app/config/environment\.ts, config/environment\.js
Added EnvironmentConfig type and supabase config (url, anonKey); removed api/cognito config and updated dev/test/prod handling.
Database Schema & Migrations
supabase-schema\.sql
Added PostgreSQL schema: palettes and colors tables, indexes, RLS policies, triggers, views, sample-data and Orbit migration helper functions.
Docs & Tests
docs/SUPABASE_MIGRATION\.md, tests/acceptance/settings/cloud-test\.ts
Added migration guide; updated acceptance tests to cover OTP flows, Supabase service stubs, and new navigation/assertions.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant Login as Login Component
    participant Supabase as Supabase Service
    participant Session as Session Service
    participant Router as Router

    rect rgb(240,248,255)
    note right of Login: OTP Request flow
    User->>Login: submit email
    Login->>Supabase: signInWithOtp(email)
    Supabase->>Supabase: initialize client from config
    Supabase-->>Login: confirmation
    Login-->>User: show OTP step
    end

    rect rgb(240,255,240)
    note right of Login: OTP Verify & Authenticate
    User->>Login: submit 6-digit code
    Login->>Supabase: verifyOtp(email, token)
    Supabase-->>Login: returns user & session
    Login->>Session: authenticate('supabase', SupabaseAuthData)
    Session-->>Login: stored
    Login->>Router: transition to settings.cloud.profile
    end
Loading
sequenceDiagram
    participant App as App/UI
    participant DataSvc as Data Service
    participant SupabaseSrc as Supabase Source
    participant Client as Supabase Client
    participant DB as PostgreSQL
    participant RT as Realtime Listener

    rect rgb(245,250,255)
    note right of DataSvc: Query palettes with colors
    App->>DataSvc: queryPalettes()
    DataSvc->>SupabaseSrc: queryPalettes()
    SupabaseSrc->>Client: SELECT palettes + colors (user filter)
    Client->>DB: execute
    DB-->>Client: rows
    SupabaseSrc-->>DataSvc: Orbit records
    DataSvc-->>App: UI update
    end

    rect rgb(245,255,245)
    note right of SupabaseSrc: Realtime sync
    SupabaseSrc->>Client: setupRealtimeSync()
    Client->>DB: subscribe
    DB-->>RT: emit change
    RT->>SupabaseSrc: onInsert/onUpdate/onDelete
    SupabaseSrc->>DataSvc: propagate change events
    DataSvc->>App: update local store/UI
    end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

"I munch code like clover, hopping fast,
Cognito leapt and Supabase arrived at last—
OTPs like moonbeams, palettes bloom bright,
Realtime whispers paint the night,
A rabbit cheers this migration's light." 🐰✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: migrating authentication from AWS Cognito to Supabase with OTP. It accurately reflects the primary objective of the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

- Fix ESLint errors in supabase service (side-effects, unsafe return)
- Fix unused vars and unnecessary type assertions
- Convert register component to template-only
- Add electron-app/ember-test to lint ignore files (build artifacts)
- Format files with prettier
- Add sort_index column to palettes table in schema
- Update remote.ts to transform index <-> sort_index
- Update palettes_with_colors view to include sort_index
@chuckcarpenter chuckcarpenter marked this pull request as ready for review December 29, 2025 16:35
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (14)
app/services/session.ts (1)

7-15: Consider importing SupabaseAuthData from the authenticator to avoid duplication.

This interface mirrors the one defined in app/authenticators/supabase.ts (lines 6-12). The structure here correctly wraps it in authenticated for ember-simple-auth, but the inner type could be imported.

🔎 Suggested refactor
+import type { SupabaseAuthData } from '../authenticators/supabase.ts';
+
-interface SupabaseAuthData {
-  authenticated: {
-    userId: string;
-    email: string;
-    accessToken: string;
-    refreshToken: string;
-    expiresAt: number;
-  };
-}
+interface SupabaseSessionData {
+  authenticated: SupabaseAuthData;
+}

-export default class SessionService extends BaseSessionService<SupabaseAuthData> {
+export default class SessionService extends BaseSessionService<SupabaseSessionData> {
docs/SUPABASE_MIGRATION.md (1)

45-45: Convert bare URL to proper Markdown link.

The static analysis flagged this as MD034 (no-bare-urls). Convert to a Markdown link for better accessibility.

🔎 Suggested fix
-1. Create a new Supabase project at https://supabase.com
+1. Create a new Supabase project at [supabase.com](https://supabase.com)
app/templates/settings/cloud/profile.gts (1)

9-20: Consider fallback display when email is unavailable.

If @model.email is undefined, users see "Signed in as..." with nothing below, which could be confusing. Consider displaying userId as a fallback or showing a placeholder.

🔎 Suggested improvement
       {{#if @model.email}}
         <div
           data-test-profile-detail="email"
           class="border-input-border border-t border-b py-5 text-menu-text text-sm"
         >
           {{@model.email}}
         </div>
+      {{else if @model.userId}}
+        <div
+          data-test-profile-detail="userId"
+          class="border-input-border border-t border-b py-5 text-menu-text text-sm text-gray-500"
+        >
+          User ID: {{@model.userId}}
+        </div>
       {{/if}}
app/services/data.ts (1)

199-210: Consider handling partial upload failures.

Sequential uploads without transaction semantics could leave data in an inconsistent state if palette uploads fail after colors have been added. Consider either:

  1. Wrapping in a try-catch to log/report partial failures
  2. Using Supabase transactions if atomicity is required

For the initial sync scenario (first login), this may be acceptable since a retry would re-upload, but orphaned colors could accumulate on repeated failures.

🔎 Proposed error handling improvement
         // Upload colors first
+        const uploadedColors: InitializedRecord[] = [];
+        const uploadedPalettes: InitializedRecord[] = [];
+
+        try {
           for (const color of colors) {
-            await remote.addRecord(color);
+            await remote.addRecord(color);
+            uploadedColors.push(color);
           }

           // Then upload palettes
           for (const palette of palettes) {
             await remote.addRecord(palette);
+            uploadedPalettes.push(palette);
           }
+        } catch (error) {
+          console.error(
+            '[Data Service] Partial sync failure:',
+            { uploadedColors: uploadedColors.length, uploadedPalettes: uploadedPalettes.length },
+            error
+          );
+          throw error;
+        }
config/environment.js (1)

25-28: Redundant Supabase config definition in production block.

The production block (lines 64-69) duplicates the default config (lines 25-28) with identical logic. Since the default already reads from process.env, the production override is unnecessary.

🔎 Proposed simplification
   if (environment === 'production') {
-    // Production Supabase config from environment variables
-    ENV.supabase = {
-      url: process.env.SUPABASE_URL || '',
-      anonKey: process.env.SUPABASE_ANON_KEY || '',
-    };
+    // Default supabase config already reads from process.env
+    // Add production-specific overrides here if needed
   }

Also applies to: 64-69

app/authenticators/supabase.ts (1)

20-23: Consider defensive check for supabase service lookup.

If getOwner(this) returns undefined or the service isn't registered, this will throw a less informative error at runtime when accessing methods.

🔎 Proposed defensive getter
   get supabase(): SupabaseService {
-    return getOwner(this)?.lookup('service:supabase') as SupabaseService;
+    const service = getOwner(this)?.lookup('service:supabase') as SupabaseService | undefined;
+    if (!service) {
+      throw new Error('SupabaseService not available');
+    }
+    return service;
   }
tests/acceptance/settings/cloud-test.ts (1)

162-168: Consider using a data-test attribute for error message assertion.

The selector .bg-red-400 is tied to styling and may break if the error styling changes. A data-test attribute would be more resilient.

🔎 Proposed improvement

In the login component template, add a data-test attribute:

-        <div class="bg-red-400 my-2 p-4 rounded-sm text-xs text-red-800">
+        <div class="bg-red-400 my-2 p-4 rounded-sm text-xs text-red-800" data-test-error-message>

Then update the test:

-    assert.dom('.bg-red-400').exists('error message is shown');
+    assert.dom('[data-test-error-message]').exists('error message is shown');
app/data-sources/remote.ts (1)

317-333: Sequential updates in replaceRelatedRecords could be slow for large palettes.

Each color update is a separate network call. For palettes with many colors, consider batching if Supabase supports it, though this is likely acceptable for typical palette sizes.

app/components/login.gts (2)

180-185: OTP validation could be stricter.

The validation only checks length (this.otpCode.length !== 6), but the input allows any characters. While the HTML pattern attribute provides client-side hints, consider validating numeric content in the action.

🔎 Proposed validation improvement
   @action
   async verifyOtp(): Promise<void> {
-    if (!this.otpCode || this.otpCode.length !== 6) {
-      this.errorMessage = 'Please enter the 6-digit code';
+    if (!this.otpCode || !/^\d{6}$/.test(this.otpCode)) {
+      this.errorMessage = 'Please enter a valid 6-digit code';
       return;
     }

44-48: Consider adding a data-test attribute to the error message div.

This would make test assertions more resilient (as noted in the test file review).

🔎 Proposed addition
       {{#if this.errorMessage}}
-        <div class="bg-red-400 my-2 p-4 rounded-sm text-xs text-red-800">
+        <div class="bg-red-400 my-2 p-4 rounded-sm text-xs text-red-800" data-test-error-message>
           {{this.errorMessage}}
         </div>
       {{/if}}
app/services/supabase.ts (1)

122-128: Use error instanceof AuthSessionMissingError instead of string matching.

Checking error.message?.includes('not authenticated') is fragile and will break if the Supabase SDK updates error messages. The SDK provides AuthSessionMissingError class (imported from @supabase/supabase-js) for this specific case. Use error instanceof AuthSessionMissingError or check error.name === 'AuthSessionMissingError' instead.

supabase-schema.sql (3)

5-6: Consider removing the uuid-ossp extension.

The schema uses gen_random_uuid() throughout (lines 12, 35), which is available in PostgreSQL 13+ by default and doesn't require the uuid-ossp extension. The uuid-ossp extension provides functions like uuid_generate_v4(), which aren't used here.

🔎 Verify Supabase PostgreSQL version and extension requirements
What version of PostgreSQL does Supabase use and is gen_random_uuid available by default?

57-68: Consider adding indexes for palette ordering.

The existing indexes provide good coverage. However, if palettes are frequently ordered by sort_index or queried by user with time-based sorting, consider adding:

🔎 Suggested additional indexes
-- For palette ordering by sort_index
CREATE INDEX IF NOT EXISTS idx_palettes_sort_index ON palettes(sort_index);

-- For time-based queries scoped to user (if frequently used)
CREATE INDEX IF NOT EXISTS idx_palettes_user_created ON palettes(user_id, created_at DESC);

These additions depend on your actual query patterns. The sort_index index is particularly relevant since the column was added specifically for palette ordering (per PR commits).


115-130: Consider modern PL/pgSQL syntax.

The trigger function works correctly but uses quoted language syntax. Modern PostgreSQL prefers unquoted language identifiers.

🔎 Suggested syntax update
   RETURN NEW;
 END;
-$$ language 'plpgsql';
+$$ LANGUAGE plpgsql;

Apply the same change to other functions (lines 175, 239, 261).

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 218b603 and 1e3adf9.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (29)
  • .prettierignore
  • .stylelintignore
  • app/authenticators/cognito.js
  • app/authenticators/supabase.ts
  • app/components/forgot-password.gts
  • app/components/login.gts
  • app/components/register-confirm.gts
  • app/components/register.gts
  • app/config/environment.ts
  • app/data-sources/remote.js
  • app/data-sources/remote.ts
  • app/router.ts
  • app/routes/settings/cloud/index.ts
  • app/routes/settings/cloud/profile.ts
  • app/services/cognito.js
  • app/services/data.ts
  • app/services/session.ts
  • app/services/supabase.ts
  • app/templates/settings/cloud/forgot-password.gts
  • app/templates/settings/cloud/profile.gts
  • app/templates/settings/cloud/register.gts
  • app/templates/settings/cloud/register/confirm.gts
  • app/templates/settings/cloud/register/index.gts
  • config/environment.js
  • docs/SUPABASE_MIGRATION.md
  • eslint.config.mjs
  • package.json
  • supabase-schema.sql
  • tests/acceptance/settings/cloud-test.ts
💤 Files with no reviewable changes (9)
  • app/components/forgot-password.gts
  • app/data-sources/remote.js
  • app/services/cognito.js
  • app/templates/settings/cloud/forgot-password.gts
  • app/authenticators/cognito.js
  • app/templates/settings/cloud/register/index.gts
  • app/routes/settings/cloud/index.ts
  • app/components/register-confirm.gts
  • app/templates/settings/cloud/register/confirm.gts
🧰 Additional context used
🧬 Code graph analysis (7)
app/data-sources/remote.ts (2)
app/services/supabase.ts (1)
  • SupabaseService (23-165)
app/services/session.ts (1)
  • SessionService (17-31)
app/routes/settings/cloud/profile.ts (1)
app/routes/settings/cloud/index.ts (1)
  • SettingsAccountRoute (9-26)
app/authenticators/supabase.ts (1)
app/services/supabase.ts (1)
  • SupabaseService (23-165)
app/services/supabase.ts (1)
app/config/environment.ts (1)
  • EnvironmentConfig (28-43)
app/services/session.ts (1)
app/authenticators/supabase.ts (1)
  • SupabaseAuthData (7-13)
tests/acceptance/settings/cloud-test.ts (3)
app/services/supabase.ts (1)
  • SupabaseService (23-165)
tests/helpers.ts (1)
  • waitForAll (24-38)
app/services/data.ts (1)
  • DataService (14-216)
app/services/data.ts (1)
app/data-sources/remote.ts (1)
  • SupabaseSource (55-509)
🪛 LanguageTool
docs/SUPABASE_MIGRATION.md

[grammar] ~96-~96: Ensure spelling is correct
Context: ...oDB: 1. Export your current data using Swach's export feature (Settings > Data > Expor...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.18.1)
docs/SUPABASE_MIGRATION.md

45-45: Bare URL used

(MD034, no-bare-urls)

🔇 Additional comments (31)
eslint.config.mjs (1)

54-54: LGTM! Consistent ignore pattern for compiled output.

The addition of electron-app/ember-test/**/* to the ignore list is appropriate for excluding compiled/generated test artifacts from linting.

.stylelintignore (1)

6-6: LGTM! Properly categorized under compiled output.

The addition of /electron-app/ember-test/ aligns with the corresponding ESLint and Prettier ignore patterns, ensuring consistent exclusion of generated test artifacts across all tooling.

.prettierignore (1)

7-7: LGTM! Consistent ignore pattern across all tooling.

The addition of /electron-app/ember-test/ completes the consistent exclusion of this compiled output directory across ESLint, Stylelint, and Prettier, which is the correct approach for generated test artifacts.

app/services/session.ts (1)

20-30: LGTM!

The session lifecycle hooks correctly trigger data synchronization on authentication and reset on invalidation. The void pattern is appropriate for these fire-and-forget background operations.

app/templates/settings/cloud/register.gts (1)

1-3: LGTM!

Clean template-only route that delegates to the Register component. This aligns with the simplified OTP flow where registration and login share the same email → code verification pattern.

docs/SUPABASE_MIGRATION.md (1)

106-112: Verify real-time sync is actually implemented.

The PR objectives note "real-time sync foundation" as future work, but this documentation describes it as a current feature. Ensure the documentation accurately reflects the current implementation state, or mark these as planned features.

app/templates/settings/cloud/profile.gts (1)

34-38: LGTM!

The type signature correctly reflects the updated ProfileModel from the route, maintaining type consistency across the template and route.

app/router.ts (1)

14-19: LGTM!

Route simplification correctly reflects the OTP flow: no password-related routes needed, and registration/confirmation are now handled inline. Clean structure.

app/routes/settings/cloud/profile.ts (1)

7-22: LGTM!

The model correctly extracts display-relevant fields from the session's authenticated data. The optional chaining is defensive, though the parent route (settings/cloud/index.ts) ensures authentication before reaching this route.

app/components/register.gts (1)

3-45: Clean migration to template-only OTP flow.

The component correctly explains the unified OTP flow where registration and login are merged. Both CTAs appropriately redirect to the login route since OTP handles new user creation automatically.

app/services/data.ts (1)

169-215: Good simplification of remote data sync logic.

The refactored method cleanly separates the authenticated/unauthenticated paths and the sequential upload approach (colors → palettes) respects foreign key constraints in the Supabase schema.

config/environment.js (1)

56-61: Test mock configuration looks correct.

Using explicit mock values for tests ensures test isolation from real Supabase credentials.

app/authenticators/supabase.ts (2)

43-68: Restore correctly delegates to Supabase's persisted session.

Ignoring the _data parameter and fetching fresh session/user from Supabase is the right approach since Supabase handles its own session persistence. This ensures token refresh works correctly.


29-41: Authenticate method is clean and well-structured.

Properly delegates OTP verification to the Supabase service and maps the response to the expected auth data format.

tests/acceptance/settings/cloud-test.ts (2)

64-114: Comprehensive OTP login flow test.

The test properly mocks the authentication chain (signInWithOtp → authenticate → synchronize) and verifies the complete flow including navigation to the profile page.


34-62: Good test isolation with service stubs.

Properly stubs signInWithOtp at the service level before user interaction, ensuring predictable behavior.

app/data-sources/remote.ts (4)

434-474: Well-structured transform functions with proper defaults.

The transformFromOrbit method correctly handles both palette and color types, applies sensible defaults, and ensures user_id is attached for RLS compliance.


481-508: Real-time sync implementation is clean.

Properly cleans up existing channel before creating a new subscription, preventing duplicate subscriptions. The wildcard event subscription ('*') captures all change types.


511-521: Factory pattern for SupabaseSource creation.

The default export provides a clean factory that applies standard Orbit injections and configures the source. Setting autoActivate: false is appropriate for controlled initialization.


60-66: Use standard getOwner(this) pattern instead of getOwner(injections).

The SupabaseSourceInjections interface doesn't declare an owner property, yet getOwner(injections) relies on applyStandardSourceInjections to implicitly set it. This is non-standard—all other service lookups in the codebase use getOwner(this) (see supabase.ts, authenticators/supabase.ts, routes/application.ts). Pass the owner explicitly or call getOwner(this) in the constructor after super(injections) completes.

app/services/supabase.ts (3)

34-52: Lazy initialization with proper config validation.

The client is lazily initialized on first access, and missing configuration throws a clear error message directing users to set the required environment variables.


136-143: Non-throwing signOut is the right choice.

Logging the error but not throwing ensures local state can be cleared even if the remote sign-out fails (e.g., network issues). This prevents users from getting stuck in a "logged in" state.


159-164: Good DRY approach for resendOtp.

Delegating to signInWithOtp avoids code duplication since both operations trigger the same OTP flow on Supabase's end.

app/components/login.gts (3)

63-77: Good accessibility for OTP input.

The OTP input includes proper attributes: inputmode="numeric", pattern="[0-9]*", autocomplete="one-time-code", and a screen-reader-only label. The centered tracking-widest styling is also appropriate for code entry.


225-231: Clean state reset in backToEmail.

Properly clears OTP code and messages when returning to the email step, ensuring a fresh state for retry.


210-223: Resend functionality is well-implemented.

Separate loading state (resendLoading) prevents UI confusion with the main submit button. Success message confirms the resend action.

supabase-schema.sql (5)

34-50: LGTM!

The colors table structure is well-designed with appropriate constraints, foreign keys with CASCADE deletes, and proper bounds checking for RGBA values.


75-108: LGTM!

The RLS policies provide comprehensive security coverage for all CRUD operations on both tables, correctly scoping access to the authenticated user via auth.uid().


182-206: LGTM!

The view correctly aggregates colors into a JSONB array using appropriate null handling with COALESCE and FILTER. The structure matches the Orbit.js format expected by the frontend.


241-261: LGTM!

The color migration function correctly extracts scalar values from JSONB using the ->> operator and applies appropriate type casts with proper default handling for the optional alpha channel.


1-261: Verify the schema in a Supabase test environment.

Before deploying to production, thoroughly test this schema in a Supabase environment to ensure:

  1. The UNIQUE constraint fix allows multiple regular palettes per user
  2. Sample data creation populates color_order with valid UUIDs
  3. Migration functions correctly handle various Orbit.js data formats
  4. RLS policies work correctly with Supabase's auth.uid() function

Consider creating integration tests that:

  • Create multiple regular palettes for a user (validates constraint fix)
  • Call create_sample_color_history_for_user() and verify the color_order array
  • Test the migration functions with sample Orbit.js exports

Comment on lines +36 to +43
supabase: {
url: string;
anonKey: string;
};
api?: {
host: string;
};
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add runtime assertion for supabase configuration.

The interface marks supabase as required, but there's no runtime assertion like other critical config fields (lines 7-26). Missing supabase config will cause runtime errors when the Supabase client initializes.

🔎 Suggested fix

Add this assertion after line 26:

assert(
  'supabase was not detected on your config',
  'supabase' in config &&
    typeof config.supabase === 'object' &&
    config.supabase !== null &&
    'url' in config.supabase &&
    'anonKey' in config.supabase
);
🤖 Prompt for AI Agents
In app/config/environment.ts around lines 36 to 43, the supabase config is
declared as required in the type but lacks a runtime assertion like other
critical fields; add a runtime assertion immediately after line 26 that verifies
config.supabase exists, is a non-null object, and contains both url and anonKey
keys, and throw/assert with a clear message if the check fails so missing
supabase config is caught at startup.

- Remove UNIQUE(user_id, is_color_history) constraint that prevented
  multiple regular palettes per user (FALSE values treated as identical)
- Add partial unique index that only enforces uniqueness for color
  history rows (WHERE is_color_history = TRUE)
- Move constraint to indexes section for proper organization
- Update from outdated 2.49.4 to latest stable 2.89.0
- All tests pass with the updated client
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
supabase-schema.sql (2)

158-164: Critical: Multi-row INSERT with RETURNING INTO still only captures first UUID.

This issue was flagged in the previous review and remains unresolved. PostgreSQL's RETURNING ... INTO syntax only populates variables from the first returned row, leaving color_uuid2 and color_uuid3 as NULL. This causes the color_order array (lines 168-172) to contain null UUIDs, breaking color references.

🔎 Proposed fix with separate INSERTs
-  -- Create sample colors
-  INSERT INTO colors (user_id, palette_id, name, r, g, b, a)
-  VALUES 
-    (user_uuid, palette_uuid, 'Pure Black', 0, 0, 0, 1.0),
-    (user_uuid, palette_uuid, 'Pure White', 255, 255, 255, 1.0),
-    (user_uuid, palette_uuid, 'Pure Red', 255, 0, 0, 1.0)
-  RETURNING id INTO color_uuid1, color_uuid2, color_uuid3;
+  -- Create sample colors (separate inserts to capture each UUID)
+  INSERT INTO colors (user_id, palette_id, name, r, g, b, a)
+  VALUES (user_uuid, palette_uuid, 'Pure Black', 0, 0, 0, 1.0)
+  RETURNING id INTO color_uuid1;
+  
+  INSERT INTO colors (user_id, palette_id, name, r, g, b, a)
+  VALUES (user_uuid, palette_uuid, 'Pure White', 255, 255, 255, 1.0)
+  RETURNING id INTO color_uuid2;
+  
+  INSERT INTO colors (user_id, palette_id, name, r, g, b, a)
+  VALUES (user_uuid, palette_uuid, 'Pure Red', 255, 0, 0, 1.0)
+  RETURNING id INTO color_uuid3;

234-234: Major: JSONB type mismatch in colorOrder extraction remains unresolved.

This issue was flagged in the previous review and is still present. Line 234 uses the ->> operator which extracts JSON as TEXT, then attempts to COALESCE with a JSONB literal. Since the color_order column expects JSONB (line 22), this creates a type mismatch that may cause runtime errors or implicit conversions.

🔎 Proposed fix using single-arrow operator
-    COALESCE(palette_data->>'colorOrder', '[]'::jsonb)
+    COALESCE(palette_data->'colorOrder', '[]'::jsonb)

Use -> (returns JSONB) instead of ->> (returns TEXT) when the target column is JSONB.

🧹 Nitpick comments (1)
supabase-schema.sql (1)

5-6: Optional: Remove unused uuid-ossp extension.

The schema uses gen_random_uuid() (line 12) which is a native PostgreSQL function (available since PostgreSQL 13) and doesn't require the uuid-ossp extension. The uuid-ossp extension provides functions like uuid_generate_v4(), which aren't used here.

🔎 Proposed simplification
--- Enable UUID extension for generating IDs
-CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

If you need to support PostgreSQL versions older than 13, you could use pgcrypto extension instead, which also provides gen_random_uuid().

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1e3adf9 and d06f18e.

📒 Files selected for processing (1)
  • supabase-schema.sql
⏰ 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). (1)
  • GitHub Check: Tests
🔇 Additional comments (7)
supabase-schema.sql (7)

11-27: LGTM: Palettes table definition is well-structured.

The removal of the problematic UNIQUE (user_id, is_color_history) constraint (from the previous review) correctly allows multiple regular palettes per user, while the partial unique index at lines 69-70 enforces the intended single-color-history-per-user rule.


32-48: LGTM: Colors table definition is well-constrained.

The table properly enforces valid RGBA ranges, includes appropriate CASCADE behavior for foreign keys, and maintains data integrity with NOT NULL checks.


68-70: Excellent: Partial unique index correctly enforces single color history per user.

This implementation properly addresses the previous constraint issue by ensuring uniqueness only for color history palettes (WHERE is_color_history = TRUE) while allowing unlimited regular palettes.


76-110: LGTM: Row Level Security policies are correctly implemented.

All policies properly restrict access to user-owned data using auth.uid() = user_id, with appropriate use of USING for row visibility and WITH CHECK for insert validation.


117-132: LGTM: Timestamp update triggers are properly configured.

The trigger function and table triggers follow PostgreSQL best practices for automatically maintaining updated_at timestamps.


184-208: LGTM: View efficiently preloads colors with proper user scoping.

The JSONB aggregation with FILTER and COALESCE correctly handles palettes without colors, and the auth.uid() filter ensures proper row-level security.


243-263: LGTM: Color migration function correctly extracts and casts values.

The function properly uses ->> for text extraction followed by explicit casting to the appropriate types (INTEGER for RGB, REAL for alpha), with a sensible default for the alpha channel.

PostgreSQL only populates INTO from the first returned row in a
multi-row INSERT. Split into three separate INSERT statements so
each color UUID variable is properly populated.
Use single-arrow operator (->) instead of double-arrow (->>) for
colorOrder to return JSONB instead of text, matching the column type.
@chuckcarpenter
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 31, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
supabase-schema.sql (1)

5-6: Optional: uuid-ossp extension is unused.

The schema creates the uuid-ossp extension but uses gen_random_uuid() throughout (lines 12, 33), which is available natively in PostgreSQL 13+ and Supabase. The uuid-ossp extension provides uuid_generate_v4() instead. Since Supabase runs modern PostgreSQL, this extension declaration is harmless but unnecessary.

🔎 Optional cleanup

You can either remove the extension or use its functions:

-CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
+-- gen_random_uuid() is available natively in PostgreSQL 13+

Or keep the extension and use its function:

-  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
+  id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e6810c and a87dda1.

📒 Files selected for processing (1)
  • supabase-schema.sql
🔇 Additional comments (7)
supabase-schema.sql (7)

32-48: LGTM: Colors table structure is well-designed.

The RGBA constraints are properly defined with appropriate ranges (0-255 for RGB, 0.0-1.0 for alpha), and the CASCADE delete ensures data integrity when palettes are removed. The foreign key relationships are correct.


54-70: LGTM: Excellent index strategy.

The use of partial indexes for boolean fields (lines 57-58, 69-70) and GIN index for JSONB queries (line 66) demonstrates good PostgreSQL optimization practices. The partial unique index correctly ensures only one color history palette per user.


76-110: LGTM: RLS policies correctly implement data isolation.

All policies properly restrict access using auth.uid() = user_id, ensuring users can only interact with their own data. The comprehensive coverage of SELECT, INSERT, UPDATE, and DELETE operations provides complete multi-tenant security.


117-132: LGTM: Standard timestamp update pattern.

The trigger function and BEFORE UPDATE triggers correctly implement automatic updated_at maintenance, following PostgreSQL best practices.


139-182: LGTM: Sample data function correctly creates initial palette.

The function properly handles UUID capture with separate INSERT statements (addressing the previous RETURNING INTO limitation), constructs JSONB color references correctly, and provides a useful onboarding experience with pre-populated sample colors.


189-213: LGTM: View efficiently aggregates palette data.

The view correctly aggregates colors into JSONB format using FILTER to exclude NULL joins, provides proper GROUP BY clauses, and includes RLS filtering for security. This will support efficient querying of palettes with their colors.


248-268: LGTM: Color migration function is well-structured.

The function correctly extracts color properties from JSONB, performs appropriate type casting, and handles the optional alpha channel with a sensible default of 1.0.

Comment on lines +11 to +27
CREATE TABLE IF NOT EXISTS palettes (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
name TEXT,
is_color_history BOOLEAN DEFAULT FALSE,
is_favorite BOOLEAN DEFAULT FALSE,
is_locked BOOLEAN DEFAULT FALSE,
selected_color_index INTEGER DEFAULT 0,
sort_index INTEGER DEFAULT 0,
color_order JSONB DEFAULT '[]'::jsonb,

-- Constraints
CONSTRAINT palettes_user_not_null CHECK (user_id IS NOT NULL),
CONSTRAINT palettes_name_length CHECK (char_length(name) <= 255)
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, verify the actual schema content
find . -name "supabase-schema.sql" -o -name "*schema*.sql" | head -5

Repository: shipshapecode/swach

Length of output: 84


🏁 Script executed:

# Search for palette name references in the codebase
rg -n "palette" --type=ts --type=js --type=tsx --type=jsx -C 2 'name' -g '!node_modules/**' -g '!dist/**' -g '!.next/**' | head -150

Repository: shipshapecode/swach

Length of output: 90


🏁 Script executed:

# Look for any schema comments or migration notes about palettes
rg -n "palette" --type=sql -C 3 | head -100

Repository: shipshapecode/swach

Length of output: 5814


🏁 Script executed:

# Check if there are any UI forms or creation logic for palettes
rg -n "palette" --type=ts --type=tsx -C 2 'create|form|input' -g '!node_modules/**' -g '!dist/**' | head -80

Repository: shipshapecode/swach

Length of output: 90


🏁 Script executed:

# Search for palette creation logic in TypeScript files
rg -n "palette" --type=ts --type=js -C 3 'create|insert|new' -g '!node_modules/**' -g '!dist/**' | head -120

Repository: shipshapecode/swach

Length of output: 120


🏁 Script executed:

# Look for palette form or creation components
fd -e ts -e js | xargs rg -l "palette" | head -10

Repository: shipshapecode/swach

Length of output: 368


🏁 Script executed:

# Check the sample data function to see how it creates palettes
sed -n '138,180p' supabase-schema.sql

Repository: shipshapecode/swach

Length of output: 1465


🏁 Script executed:

# Search for any database operations or insert statements for palettes
rg -n "palettes" --type=ts --type=js -C 2 'insert|INSERT' -g '!node_modules/**' -g '!dist/**'

Repository: shipshapecode/swach

Length of output: 116


🏁 Script executed:

# Check test fixtures for palette data
cat tests/orbit/fixtures/palettes.js

Repository: shipshapecode/swach

Length of output: 968


🏁 Script executed:

# Search for palette creation in tests
rg -n "palette" tests/ --type=js --type=ts -C 2 | head -80

Repository: shipshapecode/swach

Length of output: 5041


🏁 Script executed:

# Look at seed data
cat tests/orbit/seed.js | head -100

Repository: shipshapecode/swach

Length of output: 2939


🏁 Script executed:

# Search for any .insert or .create patterns in TypeScript/JavaScript for palettes
rg -n "palettes" . --type=ts --type=js -g '!node_modules/**' -g '!dist/**' | grep -E '(insert|create|INSERT|CREATE)' | head -20

Repository: shipshapecode/swach

Length of output: 45


🏁 Script executed:

# Look for addRecord or create patterns in application code that might create palettes
rg -n "addRecord.*palette\|createRecord.*palette" --type=ts --type=js -g '!node_modules/**' -g '!dist/**' -g '!tests/**' -B2 -A5

Repository: shipshapecode/swach

Length of output: 45


🏁 Script executed:

# Check for any form components or creation dialogs
fd -e ts -e js -g '!node_modules/**' -g '!dist/**' -g '!tests/**' | xargs rg -l "palette" | head -15

Repository: shipshapecode/swach

Length of output: 637


🏁 Script executed:

# Look at the palette model/type definition
fd -e ts -g '*palette*' -g '!node_modules/**' -g '!dist/**' -g '!tests/**'

Repository: shipshapecode/swach

Length of output: 277


🏁 Script executed:

# List the top-level directory structure
ls -la | head -30

Repository: shipshapecode/swach

Length of output: 1931


🏁 Script executed:

# Find all TypeScript files not in node_modules or tests
fd "\.ts$" --exclude "node_modules" --exclude "tests" --exclude "dist" | head -20

Repository: shipshapecode/swach

Length of output: 654


🏁 Script executed:

# Look for palette data model or service
rg -n "palette" --type=ts --exclude "node_modules" --exclude "tests" -g "!dist" | head -30

Repository: shipshapecode/swach

Length of output: 474


🏁 Script executed:

# Read the palette data model
cat app/data-models/palette.ts

Repository: shipshapecode/swach

Length of output: 743


🏁 Script executed:

# Read the palettes controller
cat app/controllers/palettes.ts

Repository: shipshapecode/swach

Length of output: 9008


🏁 Script executed:

# Check for any palette creation/form related components
fd "palette" -e ts app/ | grep -v node_modules | head -20

Repository: shipshapecode/swach

Length of output: 140


Add NOT NULL constraint to palette name field

The name field in the palettes table should have a NOT NULL constraint. While the TypeScript model defines name as non-nullable (name!: string in app/data-models/palette.ts) and the application always provides a name when creating palettes (defaulting to 'Palette'), the database schema currently allows NULL values. This creates a mismatch between the type system and database constraints, allowing potential data integrity issues from direct database operations.

name TEXT NOT NULL,
🤖 Prompt for AI Agents
In supabase-schema.sql around lines 11 to 27, the palettes.name column is
currently nullable but the app model expects a non-nullable string; change the
column definition to be NOT NULL (i.e., make name TEXT NOT NULL) and keep the
existing length check; if this is for an existing database, first update any
NULL name rows to a sensible default (e.g., 'Palette') and then ALTER TABLE to
set NOT NULL so the database matches the TypeScript model and prevents future
nulls.

Comment on lines +220 to +246
CREATE OR REPLACE FUNCTION migrate_orbit_palette_data(
user_uuid UUID,
palette_data JSONB,
color_data JSONB
)
RETURNS UUID AS $$
DECLARE
palette_uuid UUID;
BEGIN
-- Insert palette
INSERT INTO palettes (user_id, name, is_color_history, is_favorite, is_locked, selected_color_index, sort_index, color_order)
VALUES (
user_uuid,
palette_data->>'name',
(palette_data->>'isColorHistory')::BOOLEAN,
(palette_data->>'isFavorite')::BOOLEAN,
(palette_data->>'isLocked')::BOOLEAN,
COALESCE((palette_data->>'selectedColorIndex')::INTEGER, 0),
COALESCE((palette_data->>'sortIndex')::INTEGER, 0),
COALESCE(palette_data->'colorOrder', '[]'::jsonb)
)
RETURNING id INTO palette_uuid;

-- Return the new palette UUID for relationships
RETURN palette_uuid;
END;
$$ LANGUAGE plpgsql;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Remove unused color_data parameter.

The color_data parameter (line 223) is declared but never used in the function body. Since colors are migrated separately via migrate_orbit_color_data, this parameter appears to be unnecessary and misleading.

🔎 Proposed fix
 CREATE OR REPLACE FUNCTION migrate_orbit_palette_data(
   user_uuid UUID,
-  palette_data JSONB,
-  color_data JSONB
+  palette_data JSONB
 )
 RETURNS UUID AS $$
📝 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.

Suggested change
CREATE OR REPLACE FUNCTION migrate_orbit_palette_data(
user_uuid UUID,
palette_data JSONB,
color_data JSONB
)
RETURNS UUID AS $$
DECLARE
palette_uuid UUID;
BEGIN
-- Insert palette
INSERT INTO palettes (user_id, name, is_color_history, is_favorite, is_locked, selected_color_index, sort_index, color_order)
VALUES (
user_uuid,
palette_data->>'name',
(palette_data->>'isColorHistory')::BOOLEAN,
(palette_data->>'isFavorite')::BOOLEAN,
(palette_data->>'isLocked')::BOOLEAN,
COALESCE((palette_data->>'selectedColorIndex')::INTEGER, 0),
COALESCE((palette_data->>'sortIndex')::INTEGER, 0),
COALESCE(palette_data->'colorOrder', '[]'::jsonb)
)
RETURNING id INTO palette_uuid;
-- Return the new palette UUID for relationships
RETURN palette_uuid;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION migrate_orbit_palette_data(
user_uuid UUID,
palette_data JSONB
)
RETURNS UUID AS $$
DECLARE
palette_uuid UUID;
BEGIN
-- Insert palette
INSERT INTO palettes (user_id, name, is_color_history, is_favorite, is_locked, selected_color_index, sort_index, color_order)
VALUES (
user_uuid,
palette_data->>'name',
(palette_data->>'isColorHistory')::BOOLEAN,
(palette_data->>'isFavorite')::BOOLEAN,
(palette_data->>'isLocked')::BOOLEAN,
COALESCE((palette_data->>'selectedColorIndex')::INTEGER, 0),
COALESCE((palette_data->>'sortIndex')::INTEGER, 0),
COALESCE(palette_data->'colorOrder', '[]'::jsonb)
)
RETURNING id INTO palette_uuid;
-- Return the new palette UUID for relationships
RETURN palette_uuid;
END;
$$ LANGUAGE plpgsql;
🤖 Prompt for AI Agents
In supabase-schema.sql around lines 220 to 246, the function
migrate_orbit_palette_data declares a color_data parameter that is never used;
remove the unused color_data parameter from the function signature and the
RETURNS block if necessary, update the function header to accept only (user_uuid
UUID, palette_data JSONB), and update any places that call
migrate_orbit_palette_data to stop passing the extra color_data argument so
callers match the new two-argument signature.

- Add dotenv as dev dependency
- Load .env at top of config/environment.js
- Allows SUPABASE_URL and SUPABASE_ANON_KEY to be read from .env file
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
config/environment.js (1)

67-71: Consider removing redundant production override.

The production block sets ENV.supabase to the exact same values as the base configuration at lines 27-30. Since the base configuration already reads from process.env, this override is unnecessary.

🔎 Proposed simplification

The production block can be removed entirely since the base configuration already handles this:

-  if (environment === 'production') {
-    // Production Supabase config from environment variables
-    ENV.supabase = {
-      url: process.env.SUPABASE_URL || '',
-      anonKey: process.env.SUPABASE_ANON_KEY || '',
-    };
-  }

Alternatively, if you want to keep it for documentation purposes, add validation as suggested in the previous comment to ensure required values are present.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a87dda1 and a030c56.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • config/environment.js
  • package.json
⏰ 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). (1)
  • GitHub Check: Tests
🔇 Additional comments (4)
package.json (2)

147-147: LGTM!

Placing dotenv in devDependencies is appropriate since it's only needed during the build process to load environment variables in config/environment.js.


67-67: Version 2.89.0 is current and secure. The upgrade from 2.49.4 is substantial. As of January 2026, version 2.89.0 remains the latest release with no known security vulnerabilities.

config/environment.js (2)

3-3: LGTM!

Loading dotenv at the top is standard practice for local development. The dotenv package handles missing .env files gracefully, so this is safe.


59-63: LGTM!

Using mock Supabase credentials in the test environment is the correct approach to ensure tests don't require real Supabase configuration.

Comment on lines +27 to 30
supabase: {
url: process.env.SUPABASE_URL || '',
anonKey: process.env.SUPABASE_ANON_KEY || '',
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add validation for required Supabase environment variables.

The PR objectives document that SUPABASE_URL and SUPABASE_ANON_KEY are required breaking changes, but the current implementation falls back to empty strings if they're missing. This will cause cryptic runtime failures rather than failing fast with a clear error during the build or startup.

Consider adding validation that throws an error if these required values are missing in production environments.

🔎 Proposed validation for required config
+  // Validate required Supabase config in production
+  if (environment === 'production') {
+    if (!process.env.SUPABASE_URL || !process.env.SUPABASE_ANON_KEY) {
+      throw new Error(
+        'Missing required Supabase configuration. ' +
+        'SUPABASE_URL and SUPABASE_ANON_KEY environment variables must be set in production.'
+      );
+    }
+  }
+
   supabase: {
     url: process.env.SUPABASE_URL || '',
     anonKey: process.env.SUPABASE_ANON_KEY || '',
   },

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In config/environment.js around lines 27 to 30, the Supabase config silently
falls back to empty strings for SUPABASE_URL and SUPABASE_ANON_KEY; update the
file to validate these env vars and fail fast by throwing a descriptive Error if
either is missing (at least in production/startup), e.g. read
process.env.SUPABASE_URL and process.env.SUPABASE_ANON_KEY into variables, check
they are non-empty, and if not throw new Error('Missing required environment
variable SUPABASE_URL') / 'SUPABASE_ANON_KEY' (or a combined message) so the
process exits with a clear failure instead of proceeding with empty values.

# compiled output
/dist/
/electron-app/dist/
/electron-app/ember-test/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/electron-app/ember-test/

I do not think there is an electron-app/ember-test folder anymore.


# compiled output
/dist/
/electron-app/ember-test/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/electron-app/ember-test/

id: string;
userId: string;
email: string;
accessToken: string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should any of these properties be inherited from a Supabase library or something? Just wondering if they might ever change and if Supabase has standards setup around this?

@@ -0,0 +1,3 @@
import Register from '../../../components/register.gts';

<template><Register /></template>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we always email a code, instead of using passwords, I wonder if we should combine login and register into one route?

'declarations/**/*',
'dist/**/*',
'electron-app/dist/**/*',
'electron-app/ember-test/**/*',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
'electron-app/ember-test/**/*',

palettes = palettes.map((p) => {
const { id, type, attributes, relationships } = p;

if (relationships?.['colors']?.data) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm seeing a lot of deleted logic here. Was this moved somewhere or is it no longer needed? I think Dan wrote a lot of this data stuff so I am less familiar with why we were doing all this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants