<?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: operix</title>
    <description>The latest articles on DEV Community by operix (@operix).</description>
    <link>https://dev.to/operix</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%2F4229461%2F4d53484c-16e6-4b52-ba9b-6a0d962fe383.jpg</url>
      <title>DEV Community: operix</title>
      <link>https://dev.to/operix</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/operix"/>
    <language>en</language>
    <item>
      <title>`npx wix release` does not build: how we shipped yesterday's `dist/` by mistake</title>
      <dc:creator>operix</dc:creator>
      <pubDate>Fri, 06 Nov 2026 01:08:43 +0000</pubDate>
      <link>https://dev.to/operix/npx-wix-release-does-not-build-how-we-shipped-yesterdays-dist-by-mistake-4lpg</link>
      <guid>https://dev.to/operix/npx-wix-release-does-not-build-how-we-shipped-yesterdays-dist-by-mistake-4lpg</guid>
      <description>&lt;p&gt;&lt;em&gt;Wix CLI, September to November 2026, measured on three of our own apps. CLI behaviour may change.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx wix release&lt;/code&gt; uploads whatever is in &lt;code&gt;dist/&lt;/code&gt; and creates a version. It doesn't run your build. If that sentence is obvious to you, the rest of this post is about how to prove what actually went live, which turned out to be less obvious than it sounds.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happened
&lt;/h2&gt;

&lt;p&gt;On 2026-09-19 we fixed a dashboard bug in our calendar app, ran the test suite (green, type check included), and released:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx wix release -t minor -c "..."
...
Uploaded successfully
Preview created
Version 2.17.0 was created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no build step in that log. We didn't notice. We opened the dashboard to confirm the fix and saw the old behaviour.&lt;/p&gt;

&lt;p&gt;To rule out a cache, we downloaded the bundle from the new deployment host and compared it with the local &lt;code&gt;dist/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cmp /tmp/live-wrapper.js dist/&amp;lt;path&amp;gt;/wrapper.CeNtilzv.js &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;identical
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Identical, byte for byte. The local &lt;code&gt;dist/&lt;/code&gt; was from the previous evening at 20:22, before the fix. &lt;code&gt;npm run build&lt;/code&gt; changed the chunk hash from &lt;code&gt;wrapper.CeNtilzv.js&lt;/code&gt; to &lt;code&gt;wrapper.5MoEAcz1.js&lt;/code&gt;, and version 2.18.0 carried the fix. We burned a version number, no merchant was affected, and we've kept one rule since: test, build, release, in that order.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it is easy to miss
&lt;/h2&gt;

&lt;p&gt;The release output looks complete. "Uploaded successfully" reads like "built and uploaded". And a stale &lt;code&gt;dist/&lt;/code&gt; is a perfectly valid artifact: it uploads fine, the version is created, the dashboard loads. Nothing fails. The only symptom is that your change is not there, and the first explanation that comes to mind is a cache.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to prove what is live
&lt;/h2&gt;

&lt;p&gt;We nearly fooled ourselves twice while checking 2.17.0, so here is the checklist we use now.&lt;/p&gt;

&lt;p&gt;Step 1, find the current deployment host. Every release gets a new host, &lt;code&gt;&amp;lt;prefix&amp;gt;-&amp;lt;app-slug&amp;gt;.wix-host.com&lt;/code&gt;. The dashboard runs on the latest deployment the moment the release completes, so the first segment of the dashboard iframe's hostname is the current prefix. Old hosts stay online and keep answering 200 for their old assets, so measuring against an old prefix will say "nothing changed" for ever. Our first attempt took two prefixes from &lt;code&gt;.wix/debug.log&lt;/code&gt;. Both were dead and returned 500 for everything.&lt;/p&gt;

&lt;p&gt;Step 2, use a control group. On the host you believe is current, request three things: a path that does not exist, the old bundle filename, and the new bundle filename.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Request on the current host&lt;/th&gt;
&lt;th&gt;Expected&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;a path that does not exist&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old chunk &lt;code&gt;wrapper.CeNtilzv.js&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;new chunk &lt;code&gt;wrapper.5MoEAcz1.js&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;200&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If all three are 500, your host is wrong, not your release. Without that first row, a 500 for the old bundle looks like proof the new code is live. It isn't, and we were one step from writing that down.&lt;/p&gt;

&lt;p&gt;Step 3, don't &lt;code&gt;grep -c&lt;/code&gt; a minified bundle. It is one line, so the count is 0 or 1. We read a "1" as "the new string is in there". &lt;code&gt;grep -o '&amp;lt;string&amp;gt;' bundle.js | wc -l&lt;/code&gt; on the same file said 0. The most reliable check is &lt;code&gt;cmp&lt;/code&gt; against your local &lt;code&gt;dist/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Step 4, ship a build stamp. Our build now writes its timestamp into a static file under &lt;code&gt;public/&lt;/code&gt;, which each deployment host serves at &lt;code&gt;/_wix/extensions/public/build&lt;/code&gt;. Comparing a host with your local build becomes one request. We still run &lt;code&gt;cmp&lt;/code&gt; on the bundle for anything that matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running it from a script
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;wix release&lt;/code&gt; draws an interactive UI. From a non-interactive shell it dies with a raw-mode error (&lt;code&gt;NonInteractiveTerminal&lt;/code&gt;, or a complaint about &lt;code&gt;isRawModeSupported&lt;/code&gt;) and creates no version, which is at least a clean failure. Two flags are both required to make it non-interactive: &lt;code&gt;--version-type&lt;/code&gt; and &lt;code&gt;--comment&lt;/code&gt;. With only &lt;code&gt;-t minor&lt;/code&gt; it still stops at the comment prompt.&lt;/p&gt;

&lt;p&gt;From a plain &lt;code&gt;bash&lt;/code&gt; with no TTY we wrap it in &lt;code&gt;script&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
script &lt;span class="nt"&gt;-q&lt;/span&gt; /tmp/release.txt npx wix release &lt;span class="nt"&gt;-t&lt;/span&gt; minor &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"What changed, in one line"&lt;/span&gt; &amp;lt; /dev/null &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1
&lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'\r'&lt;/span&gt; &amp;lt; /tmp/release.txt | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'Version .* was created'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things we hit. Piping the output to &lt;code&gt;tail&lt;/code&gt; shows nothing until the process exits, so you cannot tell "stuck" from "working"; read the typescript file instead. And &lt;code&gt;--version-type&lt;/code&gt; accepts only &lt;code&gt;major&lt;/code&gt; and &lt;code&gt;minor&lt;/code&gt;. Passing &lt;code&gt;patch&lt;/code&gt; is rejected with &lt;code&gt;argument 'patch' is invalid. Allowed choices are major, minor.&lt;/code&gt; Every fix is therefore a minor bump, and after a &lt;code&gt;2.0.0&lt;/code&gt; the next minor is &lt;code&gt;2.1.0&lt;/code&gt;, not &lt;code&gt;2.0.1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Our release scripts also check that &lt;code&gt;HEAD&lt;/code&gt; is the intended commit and the working tree is clean before calling release, &lt;code&gt;tee&lt;/code&gt; the output into a dated report file, and grep for the "was created" line. Every call to &lt;code&gt;wix release&lt;/code&gt; creates a version. If the line is missing, read the Versions page in the Dev Center before running it again.&lt;/p&gt;

&lt;h2&gt;
  
  
  What deploys without a release, and what needs one
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend event handlers deploy immediately&lt;/strong&gt;, independent of the version you release. The CLI prints a warning about this, and the warning is accurate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Listing descriptions need a release to reach the published listing.&lt;/strong&gt; On 2026-10-16 we edited the description and the pricing-benefit lines of one app and clicked "Update Market Listing". The benefit lines went live at once; the description stayed old in the published record, and the version indicator kept saying "Up to date". A &lt;code&gt;wix release -t minor&lt;/code&gt; with unchanged code pushed the new description to all nine languages within seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pricing edits can lock releases.&lt;/strong&gt; On 2026-10-12 a change to a plan's benefit text put the app into "App submitted for review", and &lt;code&gt;wix release&lt;/code&gt; failed with &lt;code&gt;Failed Precondition … App with app id '…' is not editable&lt;/code&gt; for more than two days. A later pricing edit on another app did not trigger the lock. We don't know what decides it; we just finish code releases before touching pricing fields.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;wix preview&lt;/code&gt; lets you look before you create a version.&lt;/strong&gt; &lt;code&gt;npm run build &amp;amp;&amp;amp; npx wix preview&lt;/code&gt; prints a &lt;code&gt;versionOverrideId&lt;/code&gt;. Open the dashboard with &lt;code&gt;?apps-override=&amp;lt;that id&amp;gt;&lt;/code&gt; to see the build without releasing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where it still bites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The upload is only the start.&lt;/strong&gt; For storefront widgets, visitors receive the new deployment when Wix next renders their page, and that render can be cached for hours (we have measured up to 35; &lt;a href="https://dev.to/operix/your-wix-apps-storefront-widget-is-pinned-to-the-deployment-that-rendered-the-page-and-that-5bf5"&gt;that is a separate post&lt;/a&gt;). Verify with a cache-busting query on your own site, then &lt;code&gt;cmp&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tests that look at &lt;code&gt;dist/&lt;/code&gt; test the &lt;code&gt;dist/&lt;/code&gt; you have.&lt;/strong&gt; Some of ours scan the build output for secret names and absolute local paths. Run those after the build, or a green run is reporting on yesterday's artifact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Our build does more than &lt;code&gt;wix build&lt;/code&gt;.&lt;/strong&gt; It also strips absolute local paths out of &lt;code&gt;dist/&lt;/code&gt; before anything is uploaded. If you add steps like that, &lt;code&gt;wix release&lt;/code&gt; will not run them for you either.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The whole lesson fits in one line of a shell script: put the build in the script, because the release command won't do it for you.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written by a backend engineer with ten years of experience, building small Wix and Shopify apps as operix.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The calendar app from the 2.17.0 story is on the Wix App Market: &lt;a href="https://www.wix.com/app-market/a5d62bae-8405-4635-a985-8743d9ad50d7?utm_source=blog&amp;amp;utm_medium=article&amp;amp;utm_campaign=wix-release-does-not-build" rel="noopener noreferrer"&gt;https://www.wix.com/app-market/a5d62bae-8405-4635-a985-8743d9ad50d7?utm_source=blog&amp;amp;utm_medium=article&amp;amp;utm_campaign=wix-release-does-not-build&lt;/a&gt;&lt;/p&gt;

</description>
      <category>wix</category>
      <category>cli</category>
      <category>devops</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Your Wix widget loads from whichever deployment rendered the page, and that render can be cached for 35 hours</title>
      <dc:creator>operix</dc:creator>
      <pubDate>Fri, 06 Nov 2026 01:06:38 +0000</pubDate>
      <link>https://dev.to/operix/your-wix-apps-storefront-widget-is-pinned-to-the-deployment-that-rendered-the-page-and-that-5bf5</link>
      <guid>https://dev.to/operix/your-wix-apps-storefront-widget-is-pinned-to-the-deployment-that-rendered-the-page-and-that-5bf5</guid>
      <description>&lt;p&gt;&lt;em&gt;Observed September to November 2026 on apps built with the Wix CLI. This is platform behaviour we measured from the outside, not a documented contract. It may change.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you build Wix apps with the CLI, every &lt;code&gt;npx wix release&lt;/code&gt; creates a new deployment. Your dashboard page picks it up immediately. Your storefront widget, the custom element merchants drop onto their pages, often doesn't. For a week we believed merchants had to republish their sites to receive a widget fix. That was wrong, and the real mechanism is more useful to know.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we measured
&lt;/h2&gt;

&lt;p&gt;Each release of a CLI app gets a new static host of the form &lt;code&gt;&amp;lt;prefix&amp;gt;-&amp;lt;app-slug&amp;gt;.wix-host.com&lt;/code&gt;. The prefix changes on every release. Old deployments stay online.&lt;/p&gt;

&lt;p&gt;On 2026-09-23 we released version 1.63.0 of our search widget and, twenty minutes later, ran a read-only census over the external sites that had it installed. We found the widget on 31 published home pages. Nine of them already referenced the new prefix. Nine merchants do not republish within twenty minutes of a release they know nothing about, so "republish to receive the update" could not be the rule.&lt;/p&gt;

&lt;p&gt;We then fetched three of the sites that still referenced old prefixes, twice each: once plainly, once with a random query string.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Request&lt;/th&gt;
&lt;th&gt;&lt;code&gt;server-timing&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;age&lt;/code&gt; (seconds)&lt;/th&gt;
&lt;th&gt;Prefix in HTML&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;plain GET, site A&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cache;desc=hit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;24,582 (about 6.8 h)&lt;/td&gt;
&lt;td&gt;old&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;plain GET, site B&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cache;desc=hit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;50,198 (about 13.9 h)&lt;/td&gt;
&lt;td&gt;old&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;plain GET, site C&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cache;desc=hit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;125,021 (about 34.7 h)&lt;/td&gt;
&lt;td&gt;old&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;same URL + &lt;code&gt;?nocache=&amp;lt;random&amp;gt;&lt;/code&gt;, all three&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cache;desc=miss&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;current (1.63.0)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So the widget's script host is not baked into the page when the merchant publishes. Wix inserts the deployment that is current &lt;em&gt;when it renders the page&lt;/em&gt;, and the rendered HTML is then served from a cache. The oldest cached render we have seen was 125,021 seconds old, a little under 35 hours. We haven't found a documented TTL, and we've seen much shorter ones: on our own development sites the HTML switched to a new prefix within 2 to 4 minutes.&lt;/p&gt;

&lt;p&gt;Two months later the picture was the same at a larger scale. On 2026-11-03, about an hour after a release of our calendar widget, we fetched the home page of 126 external sites that run it. They referenced eight different deployments of the same app on the same afternoon. The distribution was 50, 38, 22, 8, 3, 2, 2 and 1 sites per deployment. The release from an hour earlier was on two of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we got it wrong first
&lt;/h2&gt;

&lt;p&gt;On 2026-09-18, right after a release, we checked one live site: old prefix. We published that site from the editor: new prefix. We wrote down "widgets update when the merchant republishes." Three days later, right after another release, two sites served two &lt;em&gt;different&lt;/em&gt; old bundles, and then both switched to the new one within minutes while nobody touched them. The lesson for us: one read of a cached page right after a change doesn't tell you the rule. We should have read it again a few hours later and looked at the headers.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to reproduce it on your own app
&lt;/h2&gt;

&lt;p&gt;You need a published site with your widget on it and the slug part of your app host.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Which deployment does the cached render reference, and how old is it?&lt;/span&gt;
curl &lt;span class="nt"&gt;-sD&lt;/span&gt; - &lt;span class="nt"&gt;-o&lt;/span&gt; /tmp/page.html &lt;span class="s1"&gt;'https://&amp;lt;a-site-with-your-widget&amp;gt;/'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iE&lt;/span&gt; &lt;span class="s1"&gt;'^(age|server-timing):'&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oE&lt;/span&gt; &lt;span class="s1"&gt;'[a-z0-9]+-&amp;lt;your-app-slug&amp;gt;\.wix-host\.com'&lt;/span&gt; /tmp/page.html | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;

&lt;span class="c"&gt;# 2. Force a fresh render by changing the URL&lt;/span&gt;
curl &lt;span class="nt"&gt;-sD&lt;/span&gt; - &lt;span class="nt"&gt;-o&lt;/span&gt; /tmp/fresh.html &lt;span class="s2"&gt;"https://&amp;lt;a-site-with-your-widget&amp;gt;/?nocache=&lt;/span&gt;&lt;span class="nv"&gt;$RANDOM$RANDOM&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iE&lt;/span&gt; &lt;span class="s1"&gt;'^(age|server-timing):'&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oE&lt;/span&gt; &lt;span class="s1"&gt;'[a-z0-9]+-&amp;lt;your-app-slug&amp;gt;\.wix-host\.com'&lt;/span&gt; /tmp/fresh.html | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If step 1 shows &lt;code&gt;cache;desc=hit&lt;/code&gt;, a large &lt;code&gt;age&lt;/code&gt; and an old prefix, and step 2 shows &lt;code&gt;cache;desc=miss&lt;/code&gt; and the prefix of your latest release, you are looking at the same mechanism.&lt;/p&gt;

&lt;p&gt;To learn which prefix &lt;em&gt;is&lt;/em&gt; current without a browser, fetch a fresh render of your own test site the same way. If you prefer the dashboard, the first segment of the hostname of your app's dashboard iframe is the current prefix, because the dashboard always runs on the latest deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;iframe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wix-host&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}))].&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One thing to watch: the hashed chunk filenames inside the bundle are resolved by Wix's loader at run time and are not in the HTML. To see which chunk a visitor actually loaded, use &lt;code&gt;performance.getEntriesByType('resource')&lt;/code&gt; in the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we changed
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;We stopped telling merchants to republish.&lt;/strong&gt; Our dashboard used to say the site had to be "published again." It now says: &lt;em&gt;Your site's cached pages still load the older version. Wix refreshes them on its own over time; publishing the site refreshes them right away.&lt;/em&gt; That one is true. The old one asked merchants to do work because we had misread a cache.&lt;/p&gt;

&lt;p&gt;We also compare prefixes now, and we allow "unknown". The dashboard fetches the merchant's published home page (and, if the widget is not there, a few pages listed in the sitemap), extracts our prefix, and compares it with the prefix the dashboard itself is running from. On the client that is &lt;code&gt;new URL(import.meta.url).origin&lt;/code&gt;. Don't use the server's &lt;code&gt;Host&lt;/code&gt; header for this; on Wix hosting it is not the public host. The result is tri-state: &lt;code&gt;current&lt;/code&gt;, &lt;code&gt;stale&lt;/code&gt;, or &lt;code&gt;unknown&lt;/code&gt; when either side could not be read. A wrong "stale" sends a merchant off to republish for nothing, and a wrong "current" hides a fix that never arrived, so when we can't read one side we'd rather show nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We ship a build stamp.&lt;/strong&gt; The build now writes its timestamp into a static file under &lt;code&gt;public/&lt;/code&gt;, so every deployment host answers &lt;code&gt;GET /_wix/extensions/public/build&lt;/code&gt; with its own build time. Comparing two deployments no longer means diffing minified bundles. A small script fetches each known site's home page, collects the prefixes, probes each prefix's build stamp, and prints a histogram. That is where the "eight deployments across 126 sites" number came from.&lt;/p&gt;

&lt;p&gt;Another thing we stopped doing: storing a deployment host anywhere. A GitHub Actions cron of ours called our server through a repository variable that held a host. Over four days and five releases it kept running a stale build, while we believed a server fix was live for every merchant. The host is now resolved at run time.&lt;/p&gt;

&lt;p&gt;And we verify releases with a cache-busting fetch. After each release we open our own site with a fresh query string and compare the served bundle with the local &lt;code&gt;dist/&lt;/code&gt; byte for byte, using &lt;code&gt;cmp&lt;/code&gt;. Opening the page plainly and seeing the old bundle is not evidence that the release failed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What still trips us up
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cart and checkout plugins behave differently.&lt;/strong&gt; For the site plugin slots (cart, checkout), republishing alone did &lt;em&gt;not&lt;/em&gt; change the bundle. The host changed only after the merchant updated the app through the app installer and then published again (measured 2026-09-22). Those slots appear to pin the app version into the published site.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The query-string trick is not guaranteed.&lt;/strong&gt; One of our test sites kept serving the previous day's build even with &lt;code&gt;?nocache=&lt;/code&gt;. Publishing from the editor refreshed it. We don't know what made that cache stickier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefix is not version.&lt;/strong&gt; A site that had installed major version 2 of our calendar served a deployment host from the version 3 line. The host tells you which build served the file. What the widget then does depends on your own code paths, not on the host. Don't read "new prefix" as "this merchant is on the new major".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The browser has its own cache.&lt;/strong&gt; Even after Wix re-renders, your browser may hold the old page. Add a query parameter when you check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Old hosts answer 200 forever.&lt;/strong&gt; If you measure against an old prefix you'll keep seeing "nothing changed", for as long as you like.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So this is how we think about it now: a widget release reaches a visitor when Wix next renders that page, which depends on the site's cache and not on anything the merchant did. We plan our checks around that, and we wrote the dashboard text around it too.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written by a backend engineer with ten years of experience, building small Wix and Shopify apps as operix.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The calendar widget whose 126-site spread appears above is on the Wix App Market: &lt;a href="https://www.wix.com/app-market/a5d62bae-8405-4635-a985-8743d9ad50d7?utm_source=blog&amp;amp;utm_medium=article&amp;amp;utm_campaign=wix-widget-cached-render-35h" rel="noopener noreferrer"&gt;https://www.wix.com/app-market/a5d62bae-8405-4635-a985-8743d9ad50d7?utm_source=blog&amp;amp;utm_medium=article&amp;amp;utm_campaign=wix-widget-cached-render-35h&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Related: &lt;a href="https://dev.to/operix/npx-wix-release-does-not-build-how-we-shipped-yesterdays-dist-by-mistake-4lpg"&gt;&lt;code&gt;npx wix release&lt;/code&gt; does not build: how we shipped yesterday's &lt;code&gt;dist/&lt;/code&gt; by mistake&lt;/a&gt;&lt;/p&gt;

</description>
      <category>wix</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
