Part 1 of the split of #41 (see that issue for full background). Do this one first — #41's other two sub-issues build on it.
Problem
public.slugify() (defined in supabase/migrations/20260804101202_add_slug_dedupe_triggers.sql) collapses every non-[a-z0-9] character, so a name with no ASCII alphanumerics (e.g. サカナクション, Чайф, punctuation-only) yields ''. The dedupe triggers on sets/artists don't catch this: COALESCE(NULLIF(TRIM(NEW.slug), ''), public.slugify(NEW.name)) — slugify returns an empty string, not NULL, so the first such row gets slug "" and the next gets "-2". Empty slugs break slug-based .single() lookups (e.g. useSetBySlug).
Fix
In a new migration, redefine public.slugify(p_name TEXT) so that when the regex collapse yields '', it falls back to a deterministic value derived from the original name:
'n-' || LEFT(md5(LOWER(TRIM(p_name))), 8)
Same name → same slug, never empty, still IMMUTABLE. Two different names hashing to the same fallback is handled by the existing dedupe triggers (-2 suffix).
Mirror the same fallback in src/lib/slug.ts generateSlug (browser md5 is not built in — use a tiny deterministic implementation or SubtleCrypto is async, so prefer a small sync md5 util; match the SQL output byte-for-byte) only if client-side slug generation still exists when this lands; if the create paths have already migrated to DB-side generation (sibling sub-issue), the client mirror can be skipped and generateSlug left as-is for format helpers.
Also mirror in supabase/functions/diff-schedule/helpers.ts toSlug (Deno has crypto.subtle async or npm: md5; keep output identical to SQL), since diff-schedule precomputes slugs the commit step looks rows up by.
Acceptance
SELECT public.slugify('サカナクション') returns a stable non-empty slug like n-xxxxxxxx.
- Inserting two different punctuation-only-named artists yields two distinct non-empty slugs.
- Unit tests in
src/lib/slug.test.ts updated to assert the non-empty guarantee and cross-check a fixture value against the SQL output.
- Existing Latin-name behavior unchanged (
Hello World → hello-world).
Part 1 of the split of #41 (see that issue for full background). Do this one first — #41's other two sub-issues build on it.
Problem
public.slugify()(defined insupabase/migrations/20260804101202_add_slug_dedupe_triggers.sql) collapses every non-[a-z0-9]character, so a name with no ASCII alphanumerics (e.g.サカナクション,Чайф, punctuation-only) yields''. The dedupe triggers onsets/artistsdon't catch this:COALESCE(NULLIF(TRIM(NEW.slug), ''), public.slugify(NEW.name))—slugifyreturns an empty string, not NULL, so the first such row gets slug""and the next gets"-2". Empty slugs break slug-based.single()lookups (e.g.useSetBySlug).Fix
In a new migration, redefine
public.slugify(p_name TEXT)so that when the regex collapse yields'', it falls back to a deterministic value derived from the original name:Same name → same slug, never empty, still
IMMUTABLE. Two different names hashing to the same fallback is handled by the existing dedupe triggers (-2suffix).Mirror the same fallback in
src/lib/slug.tsgenerateSlug(browsermd5is not built in — use a tiny deterministic implementation or SubtleCrypto is async, so prefer a small sync md5 util; match the SQL output byte-for-byte) only if client-side slug generation still exists when this lands; if the create paths have already migrated to DB-side generation (sibling sub-issue), the client mirror can be skipped andgenerateSlugleft as-is for format helpers.Also mirror in
supabase/functions/diff-schedule/helpers.tstoSlug(Deno hascrypto.subtleasync ornpm:md5; keep output identical to SQL), since diff-schedule precomputes slugs the commit step looks rows up by.Acceptance
SELECT public.slugify('サカナクション')returns a stable non-empty slug liken-xxxxxxxx.src/lib/slug.test.tsupdated to assert the non-empty guarantee and cross-check a fixture value against the SQL output.Hello World→hello-world).