<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: forkcount</title>
    <description>The latest articles on DEV Community by forkcount (@forkcount).</description>
    <link>https://dev.to/forkcount</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4173625%2Ff1241393-48ee-4f8e-b1c8-fb110167f0fb.png</url>
      <title>DEV Community: forkcount</title>
      <link>https://dev.to/forkcount</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/forkcount"/>
    <language>en</language>
    <item>
      <title>I built a free calorie tracker with a metabolic safety floor. No card, no email, no paywall.</title>
      <dc:creator>forkcount</dc:creator>
      <pubDate>Fri, 09 Oct 2026 15:04:37 +0000</pubDate>
      <link>https://dev.to/forkcount/i-built-a-free-calorie-tracker-with-a-metabolic-safety-floor-no-card-no-email-no-paywall-4gh6</link>
      <guid>https://dev.to/forkcount/i-built-a-free-calorie-tracker-with-a-metabolic-safety-floor-no-card-no-email-no-paywall-4gh6</guid>
      <description>&lt;p&gt;Every calorie app I tried had the same problem: it let you set a target of 1,200 calories or lower and called it a plan. Some went lower. That's how people end up in the binge-starve cycle — you eat 800, your body fights back, you eat 4,000, you hate yourself, repeat.&lt;/p&gt;

&lt;p&gt;So I built one that refuses.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;ForkCount asks your height, weight, age, gender, and activity level. Then it calculates your BMR, your TDEE, and a target that keeps you above the BMR floor. If you try to set a target below that line, it won't let you.&lt;/p&gt;

&lt;p&gt;The rest is a food diary, water tracker, weight log, exercise log, weekly meal plan generator, and a community feed. You type "200g chicken and 150g rice" and it parses the food.&lt;/p&gt;

&lt;p&gt;Free. No card. No trial. No paywall. Signup is a username and password — no email required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;React + Vite + TypeScript&lt;/li&gt;
&lt;li&gt;Firebase Firestore&lt;/li&gt;
&lt;li&gt;Firebase Auth&lt;/li&gt;
&lt;li&gt;GitHub Pages&lt;/li&gt;
&lt;li&gt;No backend&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The hardest part: Firestore rejects undefined
&lt;/h2&gt;

&lt;p&gt;The error was: Function setDoc() called with invalid data. Unsupported field value: undefined&lt;/p&gt;

&lt;p&gt;Firestore refuses any document that contains an undefined value anywhere inside it. When it fails, it doesn't tell you which field. So if you're saving a nested object with 40 fields, you're searching for a needle in a haystack.&lt;/p&gt;

&lt;p&gt;The fix was a recursive cleaner routed through every write:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function cleanForFirestore(obj) {
  if (Array.isArray(obj)) return obj.map(cleanForFirestore);
  if (obj instanceof Date) return obj;
  if (obj &amp;amp;&amp;amp; typeof obj === 'object') {
    const out = {};
    for (const k in obj) {
      if (obj[k] !== undefined) out[k] = cleanForFirestore(obj[k]);
    }
    return out;
  }
  return obj;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then I wrapped setDoc, updateDoc, and addDoc so every write cleans the data first.&lt;/p&gt;

&lt;h2&gt;
  
  
  The other lesson: deploy settings, not code
&lt;/h2&gt;

&lt;p&gt;The site broke with: Failed to load module script: Expected a JavaScript-or-Wasm module script but the server responded with a MIME type of "application/octet-stream"&lt;/p&gt;

&lt;p&gt;I was staring at the code. The problem was GitHub Pages settings. It was serving my repo root — which has /src/main.tsx — instead of my built dist/ folder. The fix was to copy dist/ into a docs/ folder and point Pages at /docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd like feedback on
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Does the onboarding flow make sense the first time you see it?&lt;/li&gt;
&lt;li&gt;Is the calorie calculation right? Mifflin-St Jeor for BMR, activity multiplier for TDEE, 300–500 deficit, floor at BMR.&lt;/li&gt;
&lt;li&gt;Does it break on your phone?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;forkcount.github.io&lt;/p&gt;

&lt;p&gt;I'm 13 and I built this by directing Google AI Studio, not by writing code by hand. If something looks like a beginner mistake, it probably is. Tell me and I'll fix it.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>firebase</category>
    </item>
    <item>
      <title>I built a free calorie tracker with a metabolic safety floor. No card, no email, no paywall.</title>
      <dc:creator>forkcount</dc:creator>
      <pubDate>Fri, 09 Oct 2026 14:53:23 +0000</pubDate>
      <link>https://dev.to/forkcount/i-built-a-free-calorie-tracker-with-a-metabolic-safety-floor-no-card-no-email-no-paywall-2a65</link>
      <guid>https://dev.to/forkcount/i-built-a-free-calorie-tracker-with-a-metabolic-safety-floor-no-card-no-email-no-paywall-2a65</guid>
      <description>&lt;p&gt;Every calorie app I tried had the same problem: it let you set a target of 1,200 calories or lower and called it a plan. Some went lower. That's how people end up in the binge-starve cycle — you eat 800, your body fights back, you eat 4,000, you hate yourself, repeat.&lt;/p&gt;

&lt;p&gt;So I built one that refuses.&lt;/p&gt;

&lt;p&gt;What it does&lt;/p&gt;

&lt;p&gt;ForkCount asks your height, weight, age, gender, and activity level. Then it calculates your BMR (Mifflin-St Jeor), your TDEE (BMR × activity multiplier), and a target that keeps you above the BMR floor. If you try to set a target below that line, it won't let you. You can override it in the settings, but the app tells you what you're doing and shows the maths.&lt;/p&gt;

&lt;p&gt;The rest is a food diary, water tracker, weight log, exercise log, weekly meal plan generator, and a community feed. You type "200g chicken and 150g rice" and it parses the food. There's a barcode scanner, weight tracking, exercise logging with a kcal estimate.&lt;/p&gt;

&lt;p&gt;Free. No card. No trial. No paywall. Signup is a username and password — no email required.&lt;/p&gt;

&lt;p&gt;Stack&lt;/p&gt;

&lt;p&gt;React + Vite + TypeScript&lt;/p&gt;

&lt;p&gt;Firebase Firestore for the database&lt;/p&gt;

&lt;p&gt;Firebase Auth for accounts&lt;/p&gt;

&lt;p&gt;GitHub Pages for hosting (deployed from the docs/ folder, not the repo root)&lt;/p&gt;

&lt;p&gt;No backend. All logic is client-side.&lt;/p&gt;

&lt;p&gt;The hardest part: Firestore rejects undefined&lt;/p&gt;

&lt;p&gt;The bug that cost me the most time was this:&lt;/p&gt;

&lt;p&gt;text&lt;br&gt;
Function setDoc() called with invalid data. Unsupported field value: undefined&lt;br&gt;
Firestore refuses any document that contains an undefined value anywhere inside it. Not null — undefined. And when it fails, it doesn't tell you which field, just the document path. So if you're saving a nested object with 40 fields, you're searching for a needle in a haystack.&lt;/p&gt;

&lt;p&gt;The fix was a recursive cleaner, routed through every write in the app:&lt;/p&gt;

&lt;p&gt;js&lt;br&gt;
function cleanForFirestore(obj) {&lt;br&gt;
  if (Array.isArray(obj)) return obj.map(cleanForFirestore);&lt;br&gt;
  if (obj instanceof Date) return obj;&lt;br&gt;
  if (obj &amp;amp;&amp;amp; typeof obj === 'object') {&lt;br&gt;
    const out = {};&lt;br&gt;
    for (const k in obj) {&lt;br&gt;
      if (obj[k] !== undefined) out[k] = cleanForFirestore(obj[k]);&lt;br&gt;
    }&lt;br&gt;
    return out;&lt;br&gt;
  }&lt;br&gt;
  return obj;&lt;br&gt;
}&lt;br&gt;
Then I wrapped setDoc, updateDoc, and addDoc in a safeFirestore helper that cleans the data before every write. One import change, and no write in the app can send undefined to Firestore again.&lt;/p&gt;

&lt;p&gt;The other lesson: the deploy settings, not the code&lt;/p&gt;

&lt;p&gt;The site broke for two days with this:&lt;/p&gt;

&lt;p&gt;text&lt;br&gt;
Failed to load module script: Expected a JavaScript-or-Wasm module script but the server responded with a MIME type of "application/octet-stream"&lt;br&gt;
I was staring at the code. The problem was in GitHub Pages settings. It was serving my repo root — which has /src/main.tsx — instead of my built dist/ folder. The fix was to copy dist/ into a docs/ folder, point Pages at /docs, and stop letting it serve source.&lt;/p&gt;

&lt;p&gt;The error message was the browser refusing to run a TypeScript file as JavaScript. Once you know that, it makes sense.&lt;/p&gt;

&lt;p&gt;What I'd like feedback on&lt;/p&gt;

&lt;p&gt;Does the onboarding flow make sense the first time you see it? It's country → height/weight/age → fitness level → goal.&lt;/p&gt;

&lt;p&gt;Is the calorie calculation right? Mifflin-St Jeor for BMR, activity multiplier for TDEE, 300–500 deficit, floor at BMR. I want someone who actually knows nutrition math to sanity-check it.&lt;/p&gt;

&lt;p&gt;Does it break on your phone? I've tested on one device and I need more.&lt;/p&gt;

&lt;p&gt;forkcount.github.io&lt;/p&gt;

&lt;p&gt;I'm 13 and I built this by directing Google AI Studio, not by writing code by hand. So if something looks like a beginner mistake, it probably is. Tell me and I'll fix it.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>firebase</category>
    </item>
  </channel>
</rss>
