Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/supabase/catalog_songs/selectCatalogSongsWithArtists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ export async function selectCatalogSongsWithArtists(
album,
notes,
updated_at,
song_artists!inner (
song_artists (
artist,
accounts!inner (
accounts (
Comment on lines +58 to +60

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether current schema/types and call sites assume non-null artist accounts.

# 1) Inspect generated DB relationship/type definitions for nullable account linkage.
fd -i "database.types.ts" | xargs -I{} sh -c 'echo "=== {} ==="; rg -n -C3 "song_artists|accounts" "{}"'

# 2) Inspect this query result typing and transform.
rg -n -C4 "type SongArtistWithAccount|CatalogSongQueryResult|artists: song_artists|song_artists\\?\\.map" lib/supabase/catalog_songs/selectCatalogSongsWithArtists.ts

# 3) Find downstream usage that may assume artists entries are non-null objects.
rg -n -C2 "\\bartists\\b" -g '!**/*.test.*'

Repository: recoupable/api

Length of output: 50370


🏁 Script executed:

cat -n lib/supabase/catalog_songs/selectCatalogSongsWithArtists.ts | head -120

Repository: recoupable/api

Length of output: 3851


🏁 Script executed:

# Also check the similar selectSongsWithArtists for comparison
rg -n -A 10 "artists: (song_artists|song_artists\?\.map)" lib/supabase/songs/selectSongsWithArtists.ts

Repository: recoupable/api

Length of output: 40


🏁 Script executed:

fd -type f name "selectSongsWithArtists.ts" lib/

Repository: recoupable/api

Length of output: 229


🏁 Script executed:

# Search for the file and check the pattern
rg -n "artists:.*song_artists.*map|filter" lib/supabase/songs/

Repository: recoupable/api

Length of output: 956


🏁 Script executed:

# Confirm selectSongsWithArtists structure for comparison
cat -n lib/supabase/songs/selectSongsWithArtists.ts | sed -n '1,60p'

Repository: recoupable/api

Length of output: 1466


🏁 Script executed:

# Verify the query join syntax in both files to confirm LEFT join semantics
rg -n "song_artists|!inner" lib/supabase/catalog_songs/selectCatalogSongsWithArtists.ts

Repository: recoupable/api

Length of output: 688


Filter null accounts from LEFT-joined artist records to maintain type contract.

The query uses LEFT joins for song_artists and accounts (no !inner), which can produce null values. However, the mapping at line 109 does not filter them: song_artists?.map((sa) => sa.accounts) || [] will pass nulls into the artists array, contradicting the non-null type signature artists: Tables<"accounts">[].

Compare with selectSongsWithArtists.ts (line 48), which uses INNER joins and defensively includes .filter(Boolean). Apply the same filtering pattern here:

-      artists: song_artists?.map((sa: SongArtistWithAccount) => sa.accounts) || [],
+      artists:
+        song_artists?.flatMap((sa: SongArtistWithAccount) =>
+          sa.accounts ? [sa.accounts] : [],
+        ) || [],
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/supabase/catalog_songs/selectCatalogSongsWithArtists.ts` around lines 62
- 64, The LEFT joins on song_artists and accounts tables can produce null
values, but the mapping that extracts accounts from song_artists at line 109
does not filter them out. Update the mapping logic in the section where
song_artists is mapped to extract accounts (the line with
`song_artists?.map((sa) => sa.accounts) || []`) by adding a filter to remove
null values, ensuring the artists array maintains the non-null type contract of
`artists: Tables<"accounts">[]`. Apply the same defensive filtering pattern used
in selectSongsWithArtists.ts by chaining `.filter(Boolean)` after the map
operation.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: With accounts now LEFT-joined (no !inner), sa.accounts can be null for song_artists rows whose artist FK has no matching accounts row. The downstream .map((sa) => sa.accounts) will pass those nulls into the artists array, violating the Tables<"accounts">[] type contract. Use flatMap with a null guard (or .filter(Boolean)) to exclude nulls:

artists:
  song_artists?.flatMap((sa: SongArtistWithAccount) =>
    sa.accounts ? [sa.accounts] : [],
  ) || [],
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/supabase/catalog_songs/selectCatalogSongsWithArtists.ts, line 64:

<comment>With `accounts` now LEFT-joined (no `!inner`), `sa.accounts` can be `null` for `song_artists` rows whose `artist` FK has no matching `accounts` row. The downstream `.map((sa) => sa.accounts)` will pass those `null`s into the `artists` array, violating the `Tables<"accounts">[]` type contract. Use `flatMap` with a null guard (or `.filter(Boolean)`) to exclude nulls:
```ts
artists:
  song_artists?.flatMap((sa: SongArtistWithAccount) =>
    sa.accounts ? [sa.accounts] : [],
  ) || [],
```</comment>

<file context>
@@ -55,9 +59,9 @@ export async function selectCatalogSongsWithArtists(
+        song_artists (
           artist,
-          accounts!inner (
+          accounts (
             id,
             name,
</file context>

id,
name,
timestamp
Expand Down
Loading