<?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>Forem: Dk Usa</title>
    <description>The latest articles on Forem by Dk Usa (@bairescodeai).</description>
    <link>https://forem.com/bairescodeai</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3948401%2F98816c77-ca7e-4c8d-aa39-afb1c4f8ac3c.png</url>
      <title>Forem: Dk Usa</title>
      <link>https://forem.com/bairescodeai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/bairescodeai"/>
    <language>en</language>
    <item>
      <title>Vite predev/prebuild: chaining scripts without losing your mind</title>
      <dc:creator>Dk Usa</dc:creator>
      <pubDate>Mon, 25 May 2026 13:16:31 +0000</pubDate>
      <link>https://forem.com/bairescodeai/vite-predevprebuild-chaining-scripts-without-losing-your-mind-4j01</link>
      <guid>https://forem.com/bairescodeai/vite-predevprebuild-chaining-scripts-without-losing-your-mind-4j01</guid>
      <description>&lt;p&gt;A project I'm working on needs 3 things to happen before dev/build:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hash legal content (license, terms) to detect changes.&lt;/li&gt;
&lt;li&gt;Split a huge GeoJSON file into per-region chunks.&lt;/li&gt;
&lt;li&gt;Generate static SEO landing pages from a metadata JSON.
npm's &lt;code&gt;pre-&lt;/code&gt; scripts hook into &lt;code&gt;dev&lt;/code&gt; and &lt;code&gt;build&lt;/code&gt; automatically. But chaining 3+ steps gets messy fast.
## What I tried first&lt;/li&gt;
&lt;/ol&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
json
{
  "scripts": {
    "predev": "node scripts/hash.js &amp;amp;&amp;amp; node scripts/split.js &amp;amp;&amp;amp; node scripts/gen.js",
    "prebuild": "node scripts/hash.js &amp;amp;&amp;amp; node scripts/split.js &amp;amp;&amp;amp; node scripts/gen.js",
    "dev": "vite",
    "build": "vite build"
  }
}
Works. Two problems:

Duplication: predev and prebuild are identical. Add a 4th step and you change two places.
Hard to debug: if step 2 fails, the error reaches you but the chain stops without context.
The cleaner pattern
Single orchestrator script that runs them in order with logging:

// scripts/prebuild.js
import { spawnSync } from 'node:child_process';

const STEPS = [
  ['hash-legal', 'scripts/hash-legal-content.cjs'],
  ['split-geojson', 'scripts/split_parcelas_by_sm.cjs'],
  ['gen-landings', 'scripts/generate_landings.js'],
];

for (const [name, file] of STEPS) {
  console.log(`▶ ${name}`);
  const start = Date.now();
  const res = spawnSync('node', [file], { stdio: 'inherit' });
  if (res.status !== 0) {
    console.error(`✗ ${name} failed (exit ${res.status})`);
    process.exit(res.status);
  }
  console.log(`✓ ${name} (${Date.now() - start}ms)`);
}
Then package.json:

{
  "scripts": {
    "predev": "node scripts/prebuild.js",
    "prebuild": "node scripts/prebuild.js",
    "dev": "vite",
    "build": "vite build"
  }
}
Add a 4th step? One line in the orchestrator. Want to skip a step in dev? Filter STEPS by env var. Want timing per step? Already in the log.

Bonus: parallel steps
If two steps are independent (don't write to the same file), run them in parallel:

await Promise.all([
  runStep('hash', '...'),
  runStep('split', '...'),
]);
await runStep('gen-landings', '...');
Cuts predev time roughly in half on my project.

Small refactor, much less friction every time I add a build step.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>vite</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>node</category>
    </item>
    <item>
      <title>React.lazy + chunk errors: how to recover users stuck after a deploy</title>
      <dc:creator>Dk Usa</dc:creator>
      <pubDate>Sun, 24 May 2026 16:11:18 +0000</pubDate>
      <link>https://forem.com/bairescodeai/reactlazy-chunk-errors-how-to-recover-users-stuck-after-a-deploy-2o38</link>
      <guid>https://forem.com/bairescodeai/reactlazy-chunk-errors-how-to-recover-users-stuck-after-a-deploy-2o38</guid>
      <description>&lt;p&gt;Classic React production bug: you deploy a new bundle, user has the old HTML cached in their tab, they navigate to a route → &lt;code&gt;React.lazy()&lt;/code&gt; tries to import a chunk that no longer exists on the CDN → blank screen.&lt;br&gt;
The error in the console looks like:&lt;br&gt;
Failed to fetch dynamically imported module:&lt;br&gt;
&lt;a href="https://cdn.example.com/assets/Page-abc123.js" rel="noopener noreferrer"&gt;https://cdn.example.com/assets/Page-abc123.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The user has no way out except hard-reload, and most don't know that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;A global &lt;code&gt;error&lt;/code&gt; listener that catches chunk load errors and force-reloads the page with a cache-bust param:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
js
const CHUNK_ERROR_PATTERNS = [
  /Loading chunk \d+ failed/i,
  /Failed to fetch dynamically imported module/i,
  /Loading CSS chunk .* failed/i,
  /Importing a module script failed/i,
];
window.addEventListener('error', (e) =&amp;gt; {
  const msg = e?.message || '';
  if (CHUNK_ERROR_PATTERNS.some(rx =&amp;gt; rx.test(msg))) {
    const url = new URL(window.location.href);
    url.searchParams.set('_r', String(Date.now()));
    window.location.replace(url.toString());
  }
});
Why each part matters:

Multiple patterns: different browsers throw different messages. Safari uses "Importing a module script failed", Chrome uses "Failed to fetch dynamically imported module", older Webpack builds use "Loading chunk N failed".
Case insensitive: some browsers capitalize, some don't.
Cache-bust param: ?_r=&amp;lt;timestamp&amp;gt; forces fresh index.html, which has the new chunk hashes.
replace() not assign(): doesn't add a history entry, so back button works.
Gotchas
This catches unhandledrejection too if the lazy import throws inside a promise. Add a second listener if you see misses.
Don't trigger reload more than once: add a flag to avoid loops on a network outage.
Test it: deploy a new build, open the old tab, navigate. Should auto-recover.
It's 15 lines but saved me a real percentage of users abandoning the app post-deploy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Infinite recursion in Postgres RLS: a SECURITY DEFINER gotcha</title>
      <dc:creator>Dk Usa</dc:creator>
      <pubDate>Sun, 24 May 2026 15:04:29 +0000</pubDate>
      <link>https://forem.com/bairescodeai/infinite-recursion-in-postgres-rls-a-security-definer-gotcha-1916</link>
      <guid>https://forem.com/bairescodeai/infinite-recursion-in-postgres-rls-a-security-definer-gotcha-1916</guid>
      <description>&lt;p&gt;Spent a few hours yesterday on what looked like a haunted Postgres bug. Sharing the fix in case someone else hits it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;I had a multi-tenant app with a &lt;code&gt;profiles&lt;/code&gt; table. I wanted "team admins can see all team profiles". Naive policy:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
sql
CREATE POLICY "admin_sees_team"
    ON public.profiles FOR SELECT
    TO authenticated
    USING (
        team_id IS NOT NULL
        AND EXISTS (
            SELECT 1 FROM public.profiles me
            WHERE me.id = auth.uid()
              AND me.role = 'admin'
        )
    );
Looks innocent, right?

The bug
Every query against profiles returned:

ERROR: 42P17: infinite recursion detected in policy for relation "profiles"
The policy's EXISTS subquery hits profiles — which triggers the policy again — which hits profiles again. Postgres detects it and bails.

The fix that didn't work
My first attempt: extract the check into a SQL function with SECURITY DEFINER.

CREATE FUNCTION is_admin(uid uuid) RETURNS boolean AS $$
    SELECT EXISTS (
        SELECT 1 FROM profiles WHERE id = uid AND role = 'admin'
    );
$$ LANGUAGE sql STABLE SECURITY DEFINER;
Still recursed. Why? Postgres inlines simple SQL functions during query planning. When inlined, the SECURITY DEFINER context is lost and RLS applies to the inner query → recursion is back.

The fix that worked
Use LANGUAGE plpgsql instead. plpgsql functions are never inlined:

CREATE OR REPLACE FUNCTION is_admin(uid uuid)
RETURNS boolean
LANGUAGE plpgsql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
BEGIN
    RETURN EXISTS (
        SELECT 1 FROM profiles
        WHERE id = uid AND role = 'admin'
    );
END;
$$;
Then the policy:

CREATE POLICY "admin_sees_team"
    ON profiles FOR SELECT
    TO authenticated
    USING (team_id IS NOT NULL AND is_admin(auth.uid()));
No more recursion. The plpgsql wrapper preserves the SECURITY DEFINER context, the subquery runs as the function owner (postgres), and RLS doesn't apply.

Takeaway
When a RLS policy needs to query its own table:

Don't use a raw subquery — recursion.
SQL functions look like a fix but get inlined.
plpgsql functions with SECURITY DEFINER are the only safe way.
Hope this saves someone the 3 hours I lost.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>postgres</category>
      <category>supabase</category>
      <category>webdev</category>
      <category>sql</category>
    </item>
  </channel>
</rss>
