September 23, 2026
c3f636fde26ddef97dececf00c95f160919d875c3a8ece0b19918577827fb390 Previous:
df7e36ce Bundle: 96.1 KB Expanded the trusted postMessage origin allowlist and bumped the internal protocol version to 0.38.13.
Highlights
- The postMessage origin check in the bootstrap module now also trusts admin.shopify.cn, admin.shopifystaging.cn, and a Shopify test domain in addition to the existing myshopify and shop.dev origins.
- The bundled Shopify protocol version constant was bumped from 0.38.12+8d1813e6 to 0.38.13+38896b9f.
- No modules were added or removed in this build.
- Remaining changes across fetch, id-token, intents, print, share, shortcut, sidekick, title-bar, toast, tools, and the s-app-nav, s-app-window, ui-modal, and ui-nav-menu custom elements are internal identifier renames from the rebuild with no behavioral effect.
Infrastructure Changes
REPORT.md +8 -8
@@ -1,21 +1,21 @@
# Shopify App Bridge — Unminification Report
Generated: 2026-09-16T08:04:17.824Z
Generated: 2026-09-23T08:04:09.461Z
## Files
| File | Size | Lines | Type |
|------|------|-------|------|
| _bootstrap.js | 30.1KB | 1051 | Infrastructure |
| _bootstrap.js | 30.6KB | 1061 | Infrastructure |
| _remote-ui.js | 6.1KB | 257 | Infrastructure |
| _utilities.js | 77.5KB | 2912 | Infrastructure |
| _utilities.js | 77.3KB | 2903 | Infrastructure |
| _web-vitals.js | 11.6KB | 527 | Infrastructure |
| analytics.js | 211B | 11 | Module |
| app.js | 346B | 15 | Module |
| client.js | 303B | 15 | Module |
| environment.js | 241B | 14 | Module |
| fetch.js | 3.0KB | 94 | Module |
| id-token.js | 941B | 34 | Module |
| id-token.js | 929B | 34 | Module |
| index.js | 2.2KB | 48 | Index |
| intents.js | 3.1KB | 106 | Module |
| internal-only.js | 534B | 26 | Module |
@@ -24,7 +24,7 @@ Generated: 2026-09-16T08:04:17.824Z
| picker.js | 451B | 18 | Module |
| polaris.js | 240B | 12 | Module |
| pos.js | 5.7KB | 248 | Module |
| print.js | 545B | 25 | Module |
| print.js | 568B | 25 | Module |
| resource-picker.js | 2.8KB | 117 | Module |
| reviews.js | 365B | 16 | Module |
| s-app-nav.js | 175B | 10 | Module |
@@ -34,19 +34,19 @@ Generated: 2026-09-16T08:04:17.824Z
| scopes.js | 797B | 26 | Module |
| share.js | 1.3KB | 58 | Module |
| shopifyQL.js | 1.2KB | 55 | Module |
| shortcut.js | 461B | 24 | Module |
| shortcut.js | 474B | 24 | Module |
| sidekick.js | 5.3KB | 179 | Module |
| support.js | 508B | 21 | Module |
| telemetry.js | 935B | 35 | Module |
| title-bar.js | 8.4KB | 317 | Module |
| toast.js | 1.6KB | 71 | Module |
| tools.js | 2.1KB | 75 | Module |
| tools.js | 2.2KB | 75 | Module |
| ui-modal.js | 153B | 10 | Module |
| ui-nav-menu.js | 164B | 10 | Module |
| user.js | 940B | 37 | Module |
| visibility.js | 973B | 34 | Module |
| web-vitals.js | 1.9KB | 66 | Module |
| **Total** | **180.4KB** | **6875** | |
| **Total** | **180.8KB** | **6876** | |
## Pipeline Stages
modules/_bootstrap.js Truncated +20 -15
@@ -225,7 +225,7 @@
},
};
e = {
[createPrivateKey]: o,
[privateKeyCounter]: o,
};
} else if (t instanceof File) {
e = {
@@ -234,7 +234,7 @@
type: t.type,
lastModified: t.lastModified,
},
content: restoreProperty(t),
content: interceptProperty(t),
};
} else {
if (Array.isArray(t)) {
@@ -252,8 +252,8 @@
return e;
};
const o = (t) => {
if (t && typeof t == 'object' && createPrivateKey in t) {
if (t && typeof t == 'object' && privateKeyCounter in t) {
const e = t[createPrivateKey];
const e = t[privateKeyCounter];
return n(e);
}
return (function (t) {
@@ -270,7 +270,7 @@
typeof t.content == 'string'
);
})(t)
? new File([checkPrivateField(t.content)], t.metadata.name, {
? new File([createPrivateKey(t.content)], t.metadata.name, {
type: t.metadata.type,
lastModified: t.metadata.lastModified,
})
@@ -324,26 +324,32 @@
if (e) {
if (t.origin !== e) return;
} else {
o = new URL(t.origin).hostname;
if (
!(
(bt.test(new URL(t.origin).hostname) && t.origin !== location.origin) ||
((S() || C()
? /(^admin\.shopify\.com|^admin\.shopify\.cn|^admin\.shopifystaging\.cn|^admin\.alejandrochinashopifytest742501\.com|\.myshopify\.com|\.spin\.dev|admin\.shop\.dev|localhost|\.myshopify\.io)$/
: /(^admin\.shopify\.com|^admin\.shopify\.cn|^admin\.shopifystaging\.cn|^admin\.alejandrochinashopifytest742501\.com|\.spin\.dev|admin\.shop\.dev|localhost)$/
).test(o) &&
t.origin !== location.origin) ||
(P() && t.origin === location.origin)
)
)
return;
e = t.origin;
}
const o = t.data;
var o;
if (o != null && typeof o == 'object' && o.payload && o.type)
const r = t.data;
switch (o.type) {
if (r != null && typeof r == 'object' && r.payload && r.type)
switch (r.type) {
case 'getState':
if (n === 'getState') {
}
break;
case 'dispatch':
}
}
},
@@ -418,7 +424,7 @@
config: e,
protocol: w,
origin: a,
version: SHOPIFY_PROTOCOLS,
version: It,
data: {},
setSignals(t) {
l.applyRealSignal(t);
@@ -451,10 +457,10 @@
if (i.get('shopify-reload') && !i.get('id_token')) {
A.resolve(undefined);
{
{
idToken: ALL_PROTOCOLS,
idToken: deepClone,
fetch: deepClone,
fetch: xt,
},
[],
);
@@ -491,21 +497,21 @@
A.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;
Mt(self, 'open', function (n, e, i) {
safeAsyncCall(self, 'open', function (n, e, i) {
? window.opener.open(n, e, i)
: t.call(this, n, e, i);
});
}
function e() {
const t = D();
window.top?.postMessage(
{
@@ -549,7 +555,7 @@
held: ['Meta', 'Control'],
},
].forEach((n) => {
j({
createDeferred({
...n,
handler: () => {
window.top?.postMessage(
@@ -603,14 +609,14 @@
document.append(t);
document.close();
if (n) {
}
})();
e();
}
})();
}
function I(t, n = []) {
... (truncated)
Diff truncated at 200 lines
modules/_utilities.js Truncated +34 -38
@@ -126,7 +126,7 @@ function U() {
},
};
}
function createDeferred() {
function j() {
let t = Promise.resolve();
const n = {};
return {
@@ -152,7 +152,7 @@ function createDeferred() {
},
};
}
function j({ keys, held, handler, keyEvent: i = 'keydown' }) {
function createDeferred({ keys, held, handler, keyEvent: i = 'keydown' }) {
let o = [];
const r = (i) => {
if (keys.flat().includes(i.key)) {
@@ -551,19 +551,15 @@ function wt(t, n) {
}
}
const yt = ['apiKey', 'shop'];
const bt =
const bt = {
S() || C()
? /(^admin\.shopify\.com|\.myshopify\.com|\.spin\.dev|admin\.shop\.dev|localhost|\.myshopify\.io)$/
: /(^admin\.shopify\.com|\.spin\.dev|admin\.shop\.dev|localhost)$/;
const vt = {
TITLE_BAR: 'TITLEBAR',
WEBVITALS: 'WEB_VITALS',
};
const [e, ...i] = t.split('.');
const o = privateKeyCounter(e);
const o = gt(e);
for (const s of i) r += '::' + privateKeyCounter(s);
for (const s of i) r += '::' + gt(s);
const a = {
group: e,
type: r,
@@ -573,21 +569,21 @@ function gt(t, n) {
}
return a;
}
function privateKeyCounter(t) {
function gt(t) {
return t.replace(/([a-z])([A-Z])/g, '$1_$2').toUpperCase();
}
const createPrivateKey = '_@s';
const privateKeyCounter = '_@s';
function checkPrivateField(t) {
function createPrivateKey(t) {
const n = new Uint8Array(t.length);
for (let e = 0; e < t.length; e++) n[e] = t.charCodeAt(e);
return n.buffer;
}
const interceptProperty = globalThis.XMLHttpRequest;
const checkPrivateField = globalThis.XMLHttpRequest;
function restoreProperty(t) {
function interceptProperty(t) {
let n = null;
try {
n = URL.createObjectURL(t);
const e = new interceptProperty();
const e = new checkPrivateField();
e.overrideMimeType('text/plain; charset=x-user-defined');
e.open('GET', n, false);
e.send();
@@ -602,20 +598,20 @@ function restoreProperty(t) {
}
}
}
function Tt(t, n) {
function restoreProperty(t, n) {
if (!{}.hasOwnProperty.call(t, n))
throw new TypeError('attempted to use private field on non-instance');
return t;
}
var ORIGINAL_SYMBOL = 0;
var Tt = 0;
function Lt(t) {
function ORIGINAL_SYMBOL(t) {
return `__private_${ORIGINAL_SYMBOL++}_${t}`;
return `__private_${Tt++}_${t}`;
}
let n = null;
let e = t ? null : new Set();
var i = Lt('value');
var i = ORIGINAL_SYMBOL('value');
var o = Lt('callbacks');
var o = ORIGINAL_SYMBOL('callbacks');
class r {
constructor(t) {
var n;
@@ -627,24 +623,24 @@ function It(t = false) {
writable: true,
value: new Set(),
});
Tt(this, i)[i] = t;
restoreProperty(this, i)[i] = t;
if (!((n = e) == null)) {
n.add(this);
}
}
get value() {
return Tt(this, i)[i];
return restoreProperty(this, i)[i];
}
set value(t) {
if (t !== Tt(this, i)[i]) {
if (t !== restoreProperty(this, i)[i]) {
Tt(this, i)[i] = t;
restoreProperty(this, i)[i] = t;
Tt(this, o)[o].forEach((n) => n(t));
restoreProperty(this, o)[o].forEach((n) => n(t));
}
}
subscribe(t) {
Tt(this, o)[o].add(t);
restoreProperty(this, o)[o].add(t);
return () => {
Tt(this, o)[o].delete(t);
restoreProperty(this, o)[o].delete(t);
};
}
}
@@ -726,12 +722,12 @@ function It(t = false) {
},
};
}
const SHOPIFY_PROTOCOLS = '0.38.12+8d1813e6';
const It = '0.38.13+38896b9f';
const safeAsyncCall = Symbol();
const SHOPIFY_PROTOCOLS = Symbol();
function Mt(t, n, e) {
function safeAsyncCall(t, n, e) {
const i = t[n];
e[safeAsyncCall] = i;
e[SHOPIFY_PROTOCOLS] = i;
Object.defineProperty(t, n, {
enumerable: true,
configurable: true,
@@ -740,8 +736,8 @@ function Mt(t, n, e) {
});
return i;
}
const e = t[n][safeAsyncCall];
const e = t[n][SHOPIFY_PROTOCOLS];
if (e) {
Object.defineProperty(t, n, {
enumerable: true,
@@ -751,7 +747,7 @@ function xt(t, n) {
});
}
}
const deepClone = ({ api, protocol, internalApiPromise }) => {
const xt = ({ api, protocol, internalApiPromise }) => {
const i = self.fetch;
async function o() {
try {
@@ -770,7 +766,7 @@ const deepClone = ({ api, protocol, internalApiPromise }) => {
verified: false,
};
}
Mt(globalThis, 'fetch', async function (a, s) {
safeAsyncCall(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);
@@ -836,7 +832,7 @@ const deepClone = ({ api, protocol, internalApiPromise }) => {
return y;
});
};
const ALL_PROTOCOLS = ({ api, protocol, internalApiPromise }) => {
const deepClone = ({ api, protocol, internalApiPromise }) => {
api.idToken = async function () {
const { idToken: t } = (await internalApiPromise) || {};
const i = t
@@ -861,19 +857,19 @@ const ALL_PROTOCOLS = ({ api, protocol, internalApiPromise }) => {
return i;
};
};
const WINDOW_TARGETS = Symbol();
const ALL_PROTOCOLS = Symbol();
class Ut {
class WINDOW_TARGETS {
constructor(t, n, e, i) {
... (truncated)
Diff truncated at 200 lines
modules/_web-vitals.js Truncated +9 -9
@@ -65,42 +65,42 @@ var Je = function (t) {
}
};
};
return document.visibilityState !== 'hidden' || document.prerendering ? 1 / 0 : 0;
};
}
};
var Ye = function () {
addEventListener('visibilitychange', Qe, true);
addEventListener('prerenderingchange', Qe, true);
};
var Ge = function () {
addEventListener('visibilitychange', Ye, true);
removeEventListener('visibilitychange', Qe, true);
addEventListener('prerenderingchange', Ye, true);
removeEventListener('prerenderingchange', Qe, true);
};
var Ke = function () {
removeEventListener('visibilitychange', Ye, true);
if (Je < 0) {
removeEventListener('prerenderingchange', Ye, true);
Je = Xe();
};
Ye();
var Ze = function () {
if (Xe < 0) {
Xe = Qe();
Ge();
je(function () {
setTimeout(function () {
}, 0);
});
}
return {
get firstHiddenTime() {
},
};
};
if (document.prerendering) {
addEventListener(
'prerenderingchange',
Diff truncated at 200 lines
Module Changes
modules/fetch.js +4 -4
@@ -3,8 +3,8 @@
* Intercepted fetch with auth headers and session token refresh
*/
// Registry entry referenced as: deepClone
// Registry entry referenced as: xt
const deepClone = ({ api, protocol, internalApiPromise }) => {
const xt = ({ api, protocol, internalApiPromise }) => {
const i = self.fetch;
async function o() {
try {
@@ -23,7 +23,7 @@ const deepClone = ({ api, protocol, internalApiPromise }) => {
verified: false,
};
}
Mt(globalThis, 'fetch', async function (a, s) {
safeAsyncCall(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);
@@ -90,4 +90,4 @@ const deepClone = ({ api, protocol, internalApiPromise }) => {
});
};
const fetchModule = deepClone;
const fetchModule = xt;
modules/id-token.js +3 -3
@@ -3,8 +3,8 @@
* Session token (ID token) request/response
*/
// Registry entry referenced as: ALL_PROTOCOLS
// Registry entry referenced as: deepClone
const ALL_PROTOCOLS = ({ api, protocol, internalApiPromise }) => {
const deepClone = ({ api, protocol, internalApiPromise }) => {
api.idToken = async function () {
const { idToken: t } = (await internalApiPromise) || {};
const i = t
@@ -30,4 +30,4 @@ const ALL_PROTOCOLS = ({ api, protocol, internalApiPromise }) => {
};
};
const idTokenModule = ALL_PROTOCOLS;
const idTokenModule = deepClone;
modules/intents.js +2 -2
@@ -44,7 +44,7 @@ const intentsModule = ({ api, protocol, internalApiPromise, signalFactory }) =>
'AppFrame.propertiesEvent',
({ properties }) => {
const o = (function (t, n, e) {
return new Ut('configure', 'gid://flow/stepReference/' + t, n, () =>
return new WINDOW_TARGETS('configure', 'gid://flow/stepReference/' + t, n, () =>
e.send('AppFrame.navigateBack'),
);
})(
@@ -67,7 +67,7 @@ const intentsModule = ({ api, protocol, internalApiPromise, signalFactory }) =>
if (!i) throw Error('Cannot invoke intent');
if (!i.intents?.invoke || typeof i.intents.invoke != 'function')
throw Error('Intents are not supported');
return new CLICKABLE_TAGS(i.intents.invoke(t, n));
return new Ut(i.intents.invoke(t, n));
},
};
internalApiPromise.then((t) => {
modules/print.js +2 -2
@@ -5,9 +5,9 @@
const printModule = ({ protocol, internalApiPromise }) => {
if (S() || C()) {
Mt(self, 'print', function () {
safeAsyncCall(self, 'print', function () {
const e = document.scrollingElement?.scrollHeight || document.body.offsetHeight;
Dt(async () => {
CLICKABLE_TAGS(async () => {
const { print: i } = (await internalApiPromise) || {};
if (i) {
await i({
modules/s-app-nav.js +1 -1
@@ -3,7 +3,7 @@
* Custom <s-app-nav> element for app navigation
*/
// Registry entry referenced as: xn
// Registry entry referenced as: Mn
modules/s-app-window.js +1 -1
@@ -3,9 +3,9 @@
* Custom <s-app-window> element for app window management
*/
// Registry entry referenced as: ve
// Registry entry referenced as: be
variantLock: 'app-window',
});
modules/share.js +1 -1
@@ -6,13 +6,13 @@
const shareModule = ({ protocol, internalApiPromise }) => {
if (!S() && !C()) return;
const e = navigator.share;
Mt(navigator, 'share', async function (i) {
safeAsyncCall(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/shortcut.js +1 -1
@@ -4,10 +4,10 @@
*/
const shortcutModule = ({ protocol, internalApiPromise }) => {
j({
createDeferred({
...e,
handler: async () => {
const { shortcut: i } = (await internalApiPromise) || {};
modules/sidekick.js +3 -3
@@ -115,7 +115,7 @@ const sidekickModule = ({ api, internalApiPromise, rpcEventTarget }) => {
u(e);
e = undefined;
}
Dt(async () => {
CLICKABLE_TAGS(async () => {
const { sidekick: r } = (await internalApiPromise) || {};
if (!r || typeof r.registerToolHandler != 'function')
throw Error('Sidekick API is not available');
@@ -136,7 +136,7 @@ const sidekickModule = ({ api, internalApiPromise, rpcEventTarget }) => {
u(e);
e = undefined;
}
Dt(async () => {
CLICKABLE_TAGS(async () => {
const { sidekick: r } = (await internalApiPromise) || {};
if (!r || typeof r.registerContextCallback != 'function')
throw Error('Sidekick API is not available');
@@ -156,7 +156,7 @@ const sidekickModule = ({ api, internalApiPromise, rpcEventTarget }) => {
u(e);
e = undefined;
}
Dt(async () => {
CLICKABLE_TAGS(async () => {
const { sidekick: r } = (await internalApiPromise) || {};
if (!i) {
if (!r || typeof r.registerNavigationReadinessProvider != 'function')
modules/title-bar.js +3 -3
@@ -22,18 +22,18 @@ const titleBarModule = ({ protocol, internalApiPromise }) => {
if (e) return e.getAttribute('title') ?? document.title;
}
function r(t) {
if (n) {
const e = `${re}, ${ae}, ${se}`;
const e = `${oe}, ${re}, ${ae}`;
if (i) return void i.click();
for (const n of o) {
if (e) return void e.click();
}
}
i?.click();
}
function a(t) {
@@ -67,11 +67,11 @@ const titleBarModule = ({ protocol, internalApiPromise }) => {
const n = [t];
const e = t.startsWith('s-internal-');
return [
...SIMULATING_CLICK('s-menu')
...Dt('s-menu')
.filter((t) => t.startsWith('s-internal-') === e)
.map((t) => t + '.title-bar-menu'),
].join(', ');
@@ -114,27 +114,27 @@ const titleBarModule = ({ protocol, internalApiPromise }) => {
};
}
} else {
hideElements: false,
context: document,
});
c(t && n?.localName === qt ? qt : undefined);
c(t && n?.localName === SIMULATING_CLICK ? SIMULATING_CLICK : undefined);
if (t) {
r = {
title: t.title || o(),
};
if (t.breadcrumb) {
}
if (t.primaryAction) {
r.buttons = Object.assign(r.buttons ?? {}, {
primary: n,
});
}
hideElements: false,
context: document,
});
modules/toast.js +2 -2
@@ -6,12 +6,12 @@
const toastModule = ({ api, protocol, internalApiPromise }) => {
api.toast = {
show(t, i = {}) {
Dt(async () => {
CLICKABLE_TAGS(async () => {
const { toast: r } = (await internalApiPromise) || {};
if (r?.show)
await r.show(t, {
...i,
id: o,
});
@@ -55,7 +55,7 @@ const toastModule = ({ api, protocol, internalApiPromise }) => {
return o;
},
hide(t) {
Dt(async () => {
CLICKABLE_TAGS(async () => {
const { toast: i } = (await internalApiPromise) || {};
if (i?.hide) {
await i.hide(t);
modules/tools.js +3 -3
@@ -8,7 +8,7 @@ const toolsModule = async ({ api, internalApiPromise }) => {
api.tools = {
register(t, o) {
const abortController = new AbortController();
Dt(async () => {
CLICKABLE_TAGS(async () => {
const { tools: i } = (await internalApiPromise) || {};
if (abortController.signal.aborted) return;
if (!i || typeof i.register != 'function') throw Error('Tools API is not available');
@@ -49,7 +49,7 @@ const toolsModule = async ({ api, internalApiPromise }) => {
i.add(abortController);
map.get(t)?.();
map.delete(t);
Dt(async () => {
CLICKABLE_TAGS(async () => {
const { tools: e } = (await internalApiPromise) || {};
if (!abortController.signal.aborted) {
if (!e || typeof e.unregister != 'function') throw Error('Tools API is not available');
@@ -64,7 +64,7 @@ const toolsModule = async ({ api, internalApiPromise }) => {
map.clear();
i.forEach((t) => t.abort());
i.clear();
Dt(async () => {
CLICKABLE_TAGS(async () => {
const { tools: t } = (await internalApiPromise) || {};
if (!t || typeof t.clear != 'function') throw Error('Tools API is not available');
await t.clear();
modules/ui-modal.js +1 -1
@@ -3,7 +3,7 @@
* Custom <ui-modal> element
*/
// Registry entry referenced as: Ie
// Registry entry referenced as: Le
modules/ui-nav-menu.js +1 -1
@@ -3,7 +3,7 @@
* Custom <ui-nav-menu> element
*/
// Registry entry referenced as: $e
// Registry entry referenced as: Ie
modules/web-vitals.js +2 -2
@@ -33,14 +33,14 @@ const webVitalsModule = async ({ api, protocol, internalApiPromise, rpcEventTarg
id: i.id,
name: i.name,
value: i.value,
version: SHOPIFY_PROTOCOLS,
version: It,
});
} else {
protocol.send('WebVitals.' + t, {
id: i.id,
metricName: i.name,
value: i.value,
version: SHOPIFY_PROTOCOLS,
version: It,
});
}
if (l && i.name === 'LCP') {