July 10, 2026
6d27951e14e40f54b173b01c61d83e8d0120077ac3db072c092880df2b28b171 Previous:
6d9a048d Bundle: 91.2 KB Fixes case-sensitive link target handling and URL path normalization, and makes the shopifyQL API always available with lazy resolution.
Highlights
- The shopifyQL module now always exposes api.shopifyQL as a lazy proxy with an available() check, instead of only setting it after confirming backend support.
- Link and window.open target attributes like _blank or _self are now normalized case-insensitively, fixing save-bar interception for links with mixed-case target values.
- Fixed URL path normalization in parseUrl to collapse multiple leading slashes instead of just prepending one.
Infrastructure Changes
REPORT.md +5 -5
@@ -1,6 +1,6 @@
# Shopify App Bridge — Unminification Report
Generated: 2026-07-08T08:49:32.386Z
Generated: 2026-07-10T09:02:03.630Z
## Files
@@ -8,7 +8,7 @@ Generated: 2026-07-08T08:49:32.386Z
|------|------|-------|------|
| _bootstrap.js | 29.6KB | 1028 | Infrastructure |
| _remote-ui.js | 6.0KB | 255 | Infrastructure |
| _utilities.js | 72.9KB | 2721 | Infrastructure |
| _utilities.js | 73.7KB | 2754 | Infrastructure |
| _web-vitals.js | 11.6KB | 527 | Infrastructure |
| analytics.js | 211B | 11 | Module |
| app.js | 346B | 15 | Module |
@@ -24,7 +24,7 @@ Generated: 2026-07-08T08:49:32.386Z
| picker.js | 451B | 18 | Module |
| polaris.js | 240B | 12 | Module |
| pos.js | 5.7KB | 248 | Module |
| print.js | 575B | 25 | Module |
| print.js | 560B | 25 | Module |
| resource-picker.js | 2.8KB | 119 | Module |
| reviews.js | 365B | 16 | Module |
| s-app-nav.js | 175B | 10 | Module |
@@ -33,7 +33,7 @@ Generated: 2026-07-08T08:49:32.386Z
| scanner.js | 2.8KB | 101 | Module |
| scopes.js | 797B | 26 | Module |
| share.js | 1.3KB | 58 | Module |
| shopifyQL.js | 231B | 12 | Module |
| shopifyQL.js | 262B | 12 | Module |
| shortcut.js | 461B | 24 | Module |
| sidekick.js | 4.7KB | 154 | Module |
| support.js | 508B | 21 | Module |
@@ -46,7 +46,7 @@ Generated: 2026-07-08T08:49:32.386Z
| user.js | 940B | 37 | Module |
| visibility.js | 973B | 34 | Module |
| web-vitals.js | 1.8KB | 64 | Module |
| **Total** | **172.5KB** | **6546** | |
| **Total** | **173.3KB** | **6579** | |
## Pipeline Stages
modules/_bootstrap.js +7 -7
@@ -471,21 +471,21 @@
v.resolve(undefined);
return void (async function () {
const t = window.name.endsWith('/src');
if (n) {
window.opener = n;
window.fetch = n.fetch;
window.shopify = n.shopify;
window.polaris = n.polaris;
const t = window.open;
interceptProperty(self, 'open', function (n, e, i) {
Tt(self, 'open', function (n, e, i) {
return n != null && Dt(n).protocol !== 'https:'
? window.opener.open(n, e, i)
: t.call(this, n, e, i);
});
}
function e() {
const t = createDeferred();
window.top?.postMessage(
{
@@ -962,47 +962,47 @@
n.subscribe?.((t) => i(t));
}
});
h.send('Loading.stop');
y.ready = Promise.resolve();
})();
var re;
var ae;
var se;
var ce;
var ue;
var le = -1;
var le;
var de = function (t) {
var de;
var fe;
var pe;
var he = -1;
var me = function (t) {
addEventListener(
'pageshow',
function (n) {
if (n.persisted) {
t(n);
}
},
true,
);
};
return (
window.performance &&
performance.getEntriesByType &&
performance.getEntriesByType('navigation')[0]
);
};
return (t && t.activationStart) || 0;
};
var i = 'navigate';
i = 'back-forward-cache';
} else {
if (e) {
i = 'prerender';
} else {
if (document.wasDiscarded) {
modules/_utilities.js Truncated +13 -10
@@ -683,10 +683,10 @@ function restoreProperty(t = false) {
};
}
restoreProperty(true);
const Tt = Symbol();
const interceptProperty = Symbol();
function interceptProperty(t, n, e) {
function Tt(t, n, e) {
const i = t[n];
e[Tt] = i;
e[interceptProperty] = i;
Object.defineProperty(t, n, {
enumerable: true,
configurable: true,
@@ -696,7 +696,7 @@ function interceptProperty(t, n, e) {
return i;
}
function ORIGINAL_SYMBOL(t, n) {
const e = t[n][Tt];
const e = t[n][interceptProperty];
if (e) {
Object.defineProperty(t, n, {
enumerable: true,
@@ -725,7 +725,7 @@ const Lt = ({ api, protocol, internalApiPromise }) => {
verified: false,
};
}
interceptProperty(globalThis, 'fetch', async function (a, s) {
Tt(globalThis, 'fetch', async function (a, s) {
const request = new Request(a instanceof Request ? a.clone() : a, s);
const { appOrigins: u = [] } = api.config;
const url = new URL(request.url);
@@ -868,9 +868,8 @@ function parseUrl(t, n) {
function Dt(t, n = true) {
let url = new URL(t, location.href);
if (WINDOW_TARGETS.includes(url.protocol)) {
const t = `${url.host}${url.pathname}${url.search}`;
const t = '/' + `${url.host}${url.pathname}${url.search}`.replace(/^\/+/, '');
const n = t.startsWith('/') ? t : '/' + t;
url = new URL(`${url.protocol}${t}`);
url = new URL(`${url.protocol}${n}`);
}
if (n && url.protocol === 'app:') {
url.host = location.host;
@@ -892,17 +891,22 @@ function Wt(t, n, e) {
const o = t.getAttribute('href');
const r = t.getAttribute('target') ?? '_self';
const a = t.getAttribute('rel') ?? '';
})[0];
}
function Vt(t) {
const n = t + '';
const e = n.toLowerCase();
return ALL_PROTOCOLS.includes(e) ? e : n;
}
function zt(t) {
return t.replace(/\/+$/g, '');
}
const e = R(t);
const i = R(n);
if (e.href === i.href) return true;
return false;
if (!e.search && !i.search) return true;
if (!e.search || !i.search) return false;
@@ -912,7 +916,7 @@ function zt(t, n) {
const s = Array.from(r.entries()).sort(([t], [n]) => t.localeCompare(n));
return a.length === s.length && a.every(([t, n], e) => t === s[e][0] && n === s[e][1]);
}
const Ht = ({ internalApiPromise, saveBarManager, rpcEventTarget }) => {
const generateId = ({ internalApiPromise, saveBarManager, rpcEventTarget }) => {
if (self.shopify != null) {
self.shopify[Symbol.for('navigationVersion')] = 2;
}
@@ -922,7 +926,7 @@ const Ht = ({ internalApiPromise, saveBarManager, rpcEventTarget }) => {
const { navigation: i } = await internalApiPromise;
if (saveBarManager.isSaveBarVisible) return;
const r = Dt(new URL(e.detail.destination.url, location.href));
if (zt(r, Dt(location.href))) return;
... (truncated)
Diff truncated at 200 lines
Module Changes
modules/fetch.js +1 -1
@@ -23,7 +23,7 @@ const Lt = ({ api, protocol, internalApiPromise }) => {
verified: false,
};
}
interceptProperty(globalThis, 'fetch', async function (a, s) {
Tt(globalThis, 'fetch', async function (a, s) {
const request = new Request(a instanceof Request ? a.clone() : a, s);
const { appOrigins: u = [] } = api.config;
const url = new URL(request.url);
modules/navigation.js +2 -2
@@ -4,13 +4,13 @@
*/
const navigationModule = (t) => {
const n = generateId(t);
const n = Jt(t);
t.internalApiPromise.then((e) => {
const { navigation: i } = e || {};
if (i?.version === 2)
try {
n();
Ht(t);
generateId(t);
} catch (err) {
console.error('Failed to set up navigation: ' + err);
}
modules/print.js +1 -1
@@ -4,8 +4,8 @@
*/
const printModule = ({ protocol, internalApiPromise }) => {
interceptProperty(self, 'print', function () {
Tt(self, 'print', function () {
const e = document.scrollingElement?.scrollHeight || document.body.offsetHeight;
SHOPIFY_PROTOCOLS(async () => {
const { print: i } = (await internalApiPromise) || {};
modules/s-app-nav.js +1 -1
@@ -3,7 +3,7 @@
* Custom <s-app-nav> element for app navigation
*/
// Registry entry referenced as: pn
// Registry entry referenced as: hn
modules/share.js +1 -1
@@ -4,15 +4,15 @@
*/
const shareModule = ({ protocol, internalApiPromise }) => {
const e = navigator.share;
interceptProperty(navigator, 'share', async function (i) {
Tt(navigator, 'share', async function (i) {
if (!i) return e.call(navigator, i);
const { share: o } = (await internalApiPromise) || {};
const { title: r, text: a, url: s } = i;
if (!o)
return new Promise((n, e) => {
const abortController = new AbortController();
const { signal: c } = abortController;
function u(t) {
modules/shopifyQL.js +5 -5
@@ -3,9 +3,9 @@
* ShopifyQL query interface
*/
const shopifyQLModule = async ({ api, internalApiPromise }) => {
const shopifyQLModule = ({ api, internalApiPromise }) => {
const { shopifyQL: e } = (await internalApiPromise) || {};
const e = Promise.resolve(internalApiPromise)
if (e) {
.then((t) => t?.shopifyQL ?? null)
api.shopifyQL = e;
.catch(() => null);
}
api.shopifyQL = ne(e);
};
modules/title-bar.js +1 -1
@@ -24,16 +24,16 @@ const titleBarModule = ({ protocol, internalApiPromise }) => {
function r(t) {
const n = document.querySelector('s-page');
if (n) {
const e = `${Fn}, ${Un}, ${_n}`;
const e = `${Un}, ${_n}, ${Bn}`;
if (i) return void i.click();
const o = Array.from(document.querySelectorAll('s-menu, s-button-group'));
for (const n of o) {
if (e) return void e.click();
}
}
i?.click();
}
function a(t) {
modules/ui-modal.js +1 -1
@@ -3,7 +3,7 @@
* Custom <ui-modal> element
*/
// Registry entry referenced as: ne
// Registry entry referenced as: re
modules/ui-nav-menu.js +1 -1
@@ -3,7 +3,7 @@
* Custom <ui-nav-menu> element
*/
// Registry entry referenced as: ee
// Registry entry referenced as: ae