TLDR:
/_([a-z])/gskips underscores followed by digits.run_rate_7dbecomesrunRate_7d. TypeScript doesn't notice.?? 0turnsundefinedinto a real-looking zero. Your whole dashboard lies quietly.
the wall
I pulled up the supply-chain dashboard — my internal ops app (a Next.js + Supabase inventory tracker for an ecommerce business, an ecommerce brand I work with) — and the run-rate columns were completely zeroed.
Every single product. 0.0/day.
Days Left still showed real values — 52d, 86d, 818d. The last-10-days velocity looked fine. Forecast, fine. Just both rate columns: zeros, all the way down.
the wrong turn
My first instinct? Stale snapshot. Sync outage again.
We'd had a bad snapshot before. I went straight for the usual suspects — checked the last-written timestamps, looked for a broken cron, poked at the data pipeline.
Nothing. The DB rows looked completely healthy.
That's when the contradiction stopped me.
the pivot
Here's the thing: Days Left and run rate are both calculated from the same snapshot row, written by the same route, from the same data. If there were a write bug, both would be broken.
But only the rates were zero. Days Left was real.
That means the DB was never wrong. Something was breaking only on the read — specifically on the name of the field being read.
the actual bug
I dug into src/lib/supabase/case.ts, the utility that converts Supabase's snake_case column names into JavaScript-friendly camelCase keys.
The regex: /_([a-z])/g
Spot it?
[a-z] only matches lowercase letters. An underscore followed by a digit — _7, _30 — never matches.
So:
days_of_inventory→daysOfInventory✓ (letter after the underscore)run_rate_7d→runRate_7d✗ (digit after the last underscore — never transformed)
Every time my code read snapshot.runRate7d, JavaScript returned undefined. And I had ?? 0 as a fallback — because zero seemed like a safe default for a rate.
So undefined silently became 0.0/day and nothing threw. TypeScript didn't notice either — camelCaseRow returns result as T, so the compiler just trusts the declared type and never sees the runtime key mismatch. A passing typecheck was zero evidence.
The fix was one character: /_([a-z0-9])/g.
Widened the character class to include digits. Added a regression test (src/lib/__tests__/case.test.ts). tsc clean, 61/61 tests green. Done.
why this one stuck
Three things compounded to make this invisible:
?? 0turnedundefinedinto plausible data. No error, noNaN, just a real-looking business number.- TypeScript cast silently trusted the declared type. The key mismatch lived entirely at runtime. The types said it was fine.
- The columns that broke all had digit segments (
run_rate_7d,run_rate_30d). The one that worked didn't (days_of_inventory). That per-key selectivity is the fingerprint of a name-transform bug — not a sync issue, not bad data.
When some fields in a row are right and others are zero, don't chase the data source first. Look at what's different about the names. The answer is usually in the transform.
P.S. Post-fix:
runRate7d × daysOfInventorylands right back near actual stock on hand for every product. The data was right the whole time.