August 7, 2026
166e1f5d310441d6c56e84451c775bceb3a69c9b9b5fdae89355cf49c23af870 Previous:
f90ae585 Bundle: 93.3 KB Bootstrap now captures an admin_theme parameter, persists it to sessionStorage, and exposes it globally.
Highlights
- Bootstrap reads a new admin_theme URL parameter and stores it in sessionStorage under the key app-bridge-admin-theme.
- When the admin_theme parameter is absent from the URL, App Bridge now falls back to a previously cached sessionStorage value.
- The captured value is exposed as globalThis.__shopifyAdminTheme so other code in the page can read the active admin theme.
- The admin_theme parameter was added to the list of query parameters App Bridge preserves when rewriting URLs.
- Remaining changes across modules like fetch, pos, toast, and tools are internal identifier renames with no behavior changes.
Infrastructure Changes
REPORT.md +8 -8
@@ -1,14 +1,14 @@
# Shopify App Bridge — Unminification Report
Generated: 2026-08-05T08:53:06.590Z
Generated: 2026-08-07T08:21:59.333Z
## Files
| File | Size | Lines | Type |
|------|------|-------|------|
| _bootstrap.js | 29.7KB | 1033 | Infrastructure |
| _bootstrap.js | 30.1KB | 1052 | Infrastructure |
| _remote-ui.js | 6.0KB | 255 | Infrastructure |
| _remote-ui.js | 6.1KB | 257 | Infrastructure |
| _utilities.js | 76.0KB | 2848 | Infrastructure |
| _utilities.js | 76.1KB | 2849 | Infrastructure |
| _web-vitals.js | 11.6KB | 527 | Infrastructure |
| analytics.js | 211B | 11 | Module |
| app.js | 346B | 15 | Module |
@@ -18,13 +18,13 @@ Generated: 2026-08-05T08:53:06.590Z
| id-token.js | 908B | 34 | Module |
| index.js | 2.2KB | 48 | Index |
| intents.js | 3.1KB | 106 | Module |
| internal-only.js | 539B | 26 | Module |
| internal-only.js | 542B | 26 | Module |
| loading.js | 605B | 31 | Module |
| navigation.js | 408B | 19 | Module |
| picker.js | 451B | 18 | Module |
| polaris.js | 240B | 12 | Module |
| pos.js | 5.7KB | 248 | Module |
| print.js | 557B | 25 | Module |
| print.js | 562B | 25 | Module |
| resource-picker.js | 2.8KB | 117 | Module |
| reviews.js | 365B | 16 | Module |
| s-app-nav.js | 175B | 10 | Module |
@@ -40,13 +40,13 @@ Generated: 2026-08-05T08:53:06.590Z
| telemetry.js | 935B | 35 | Module |
| title-bar.js | 7.6KB | 290 | Module |
| toast.js | 1.6KB | 71 | Module |
| tools.js | 2.2KB | 75 | Module |
| tools.js | 2.1KB | 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** | **176.2KB** | **6696** | |
| **Total** | **176.8KB** | **6718** | |
## Pipeline Stages
modules/_bootstrap.js Truncated +27 -8
@@ -140,17 +140,36 @@
params: searchParams,
};
})();
(function (t) {
try {
sessionStorage.setItem('app-bridge-config', JSON.stringify(t));
} catch (err) {}
const e = t.host ? atob(t.host) : t.shop;
(function (e) {
const i = new URL('https://' + e).origin;
const o = (() => {
const a =
const o = e.get(t);
if (o) {
try {
sessionStorage.setItem(n, o);
} catch {}
return o;
}
try {
return sessionStorage.getItem(n) || undefined;
} catch {
return;
}
})();
if (o) {
globalThis.__shopifyAdminTheme = o;
}
})(o);
const i = e.host ? atob(e.host) : e.shop;
const a = new URL('https://' + i).origin;
const c =
globalThis.shopify?.transport ||
? {
addEventListener: globalThis.addEventListener.bind(globalThis),
removeEventListener: globalThis.removeEventListener.bind(globalThis),
@@ -168,11 +187,11 @@
removeEventListener: globalThis.removeEventListener.bind(globalThis),
postMessage: globalThis.parent.postMessage.bind(globalThis.parent),
});
const c = ORIGINAL_SYMBOL();
const l = It();
const n = t.decodeSignal;
return (t) => {
const o = (t, n = new Map()) => {
if (n.has(t)) return n.get(t);
n.set(t, undefined);
@@ -201,12 +220,12 @@
});
return () => {
e();
};
},
};
e = {
[gt]: i,
[createPrivateKey]: i,
};
} else if (t instanceof File) {
e = {
@@ -215,13 +234,13 @@
type: t.type,
lastModified: t.lastModified,
},
content: checkPrivateField(t),
content: Tt(t),
};
} else {
if (Array.isArray(t)) {
e = t.map((t) => o(t, n));
} else {
e = Object.keys(t).reduce((e, i) => {
e[i] = o(t[i], n);
return e;
@@ -233,8 +252,8 @@
return e;
};
const i = (t) => {
if (t && typeof t == 'object' && gt in t) {
if (t && typeof t == 'object' && createPrivateKey in t) {
const e = t[gt];
const e = t[createPrivateKey];
return n(e);
... (truncated)
Diff truncated at 200 lines
modules/_remote-ui.js Truncated +4 -2
@@ -4,20 +4,22 @@
* @description Remote procedure call and memory management for remote rendering
*/
const t = 'admin_theme';
const n = 'app-bridge-admin-theme';
const retainSymbol =
Symbol.for('RemoteUi::Retain'); /* Symbol for retaining a remote object in memory */
const releaseSymbol =
Symbol.for('RemoteUi::Release'); /* Symbol for releasing a remote object from memory */
const retainedBySymbol =
Symbol.for('RemoteUi::RetainedBy'); /* Symbol for tracking which retainers hold this object */
constructor() {
this.memoryManaged = new Set();
}
}
release() {
for (const t of this.memoryManaged) {
@@ -27,151 +29,151 @@ class o {
this.memoryManaged.clear();
}
}
}
}
}
const t = n.reduce((t, n) => a(n, e, o) || t, s);
const e = t.reduce((t, e) => c(e, n, o) || t, r);
}
}
}
}
}
t[releaseSymbol]();
}
if (Array.isArray(t)) {
}
}
}
}
if (t == null || typeof t != 'object') return false;
const n = Object.getPrototypeOf(t);
return n == null || n === Object.prototype;
}
const remoteFunctionMarker = '_@f'; /* Function marker for RPC serialization */
const map = new Map();
const s = new Map();
const c = new Map();
return {
n.map((n) => {
o.map((n) => {
}),
];
}
return {
[r]: a,
};
}, {}),
];
... (truncated)
Diff truncated at 200 lines
modules/_utilities.js Truncated +21 -20
@@ -4,16 +4,16 @@
* error types, DOM validation framework, navigation menu base
*/
return `${h()}-${h()}-${h()}-${h()}`;
return `${w()}-${w()}-${w()}-${w()}`;
}
return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16);
}
return t.toLowerCase().replace(/-+(.)/g, (t, n) => n.toUpperCase());
}
if (n)
for (let e in n) {
const o = n[e];
@@ -36,41 +36,41 @@ class v extends b {
}).innerHTML = '<style>:host{display: none;}</style><slot></slot>';
}
}
return t.replace(/-([a-z])/gi, (t, n) => n.toUpperCase());
}
try {
return navigator.userAgent.toLowerCase().includes(t.toLowerCase());
} catch (err) {
return false;
}
}
function E() {
return A('Unframed') && 'MobileWebView' in window;
}
function k() {
return A('Shopify Mobile');
}
function S() {
return A('Extensibility');
return k('Unframed') && 'MobileWebView' in window;
}
function T() {
return k('Shopify Mobile');
}
function P() {
return A('Shopify POS');
return k('Extensibility');
}
function C() {
return k('Shopify POS');
}
return {
apiKey: t,
scope: n,
mode: e,
};
})();
'hmac',
'locale',
'protocol',
@@ -82,13 +82,14 @@ const O = [
'embedded',
'appLoadId',
'link_source',
t,
];
const url = new URL(t);
return url;
}
'FailedAuthentication',
'InvalidAction',
'InvalidActionType',
@@ -101,12 +102,12 @@ const x = [
'UnexpectedAction',
'UnsupportedOperation',
];
t.subscribe('Error.' + o, n, e);
});
}
function U() {
function subscribeAllErrors() {
let t;
let n = false;
const promise = new Promise((n) => {
@@ -125,7 +126,7 @@ function U() {
},
};
}
function subscribeAllErrors() {
function createDeferred() {
let t = Promise.resolve();
const n = {};
return {
@@ -134,7 +135,7 @@ function subscribeAllErrors() {
},
has: (t) => !!n[t],
add(e) {
const o = U();
const o = subscribeAllErrors();
n[e] = o;
t = t.then(() => o.promise);
},
@@ -193,31 +194,31 @@ function _({ keys, held, handler, keyEvent: o = 'keydown' }) {
});
};
}
function createDeferred() {
function j() {
const t = window.shopify.config.host;
return 'https://' + atob(t);
}
const N = Symbol();
const j = Symbol();
const D = Symbol();
const q = Symbol();
const W = 'data-save-bar';
const W = Symbol();
const H = 'data-discard-confirmation';
const H = Symbol();
const V = 'ui-save-bar';
const V = 'data-save-bar';
const z = 'update';
const z = 'data-discard-confirmation';
const G = 'valuemodified';
const G = 'ui-save-bar';
const J = 'update';
const K = 'valuemodified';
const set = new Set();
for (const t of set) t.settle();
}
let o = false;
let i = null;
function r() {
... (truncated)
Diff truncated at 200 lines
modules/_web-vitals.js Truncated +8 -8
@@ -65,42 +65,42 @@ var Me = function (t) {
}
};
};
return document.visibilityState !== 'hidden' || document.prerendering ? 1 / 0 : 0;
};
}
};
};
var Ue = function () {
removeEventListener('visibilitychange', xe, true);
removeEventListener('prerenderingchange', xe, true);
};
var Fe = function () {
if (Oe < 0) {
removeEventListener('visibilitychange', Ue, true);
Oe = Re();
removeEventListener('prerenderingchange', Ue, true);
$e();
};
Ee(function () {
var Be = function () {
if (xe < 0) {
xe = $e();
_e();
Se(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: safeAsyncCall
// Registry entry referenced as: SHOPIFY_PROTOCOLS
const safeAsyncCall = ({ api, protocol, internalApiPromise }) => {
const SHOPIFY_PROTOCOLS = ({ api, protocol, internalApiPromise }) => {
const o = self.fetch;
async function i() {
try {
@@ -23,7 +23,7 @@ const safeAsyncCall = ({ 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 safeAsyncCall = ({ api, protocol, internalApiPromise }) => {
});
};
const fetchModule = safeAsyncCall;
const fetchModule = SHOPIFY_PROTOCOLS;
modules/id-token.js +1 -1
@@ -3,8 +3,8 @@
* Session token (ID token) request/response
*/
// Registry entry referenced as: xt
// Registry entry referenced as: Ut
api.idToken = async function () {
const { idToken: t } = (await internalApiPromise) || {};
const o = t
modules/intents.js +2 -2
@@ -44,7 +44,7 @@ const intentsModule = ({ api, protocol, internalApiPromise, signalFactory }) =>
'AppFrame.propertiesEvent',
({ properties }) => {
const i = (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 (!o) throw Error('Cannot invoke intent');
if (!o.intents?.invoke || typeof o.intents.invoke != 'function')
throw Error('Intents are not supported');
return new WINDOW_TARGETS(o.intents.invoke(t, n));
return new CLICKABLE_TAGS(o.intents.invoke(t, n));
},
};
internalApiPromise.then((t) => {
modules/internal-only.js +1 -1
@@ -6,7 +6,7 @@
const internalOnlyModule = ({ api, internalApiPromise }) => {
const e = {
async show(t, e) {
const o = ALL_PROTOCOLS(e);
const o = SIMULATING_CLICK(e);
const i = await internalApiPromise;
if (i && i.internalModal) {
await i.internalModal.show?.(t, o);
modules/pos.js +4 -4
@@ -6,9 +6,9 @@
const posModule = ({ api, protocol }) => {
const e = new Set();
async function o(t, o) {
const abortController = new AbortController();
const a = U();
const a = subscribeAllErrors();
protocol.subscribe(
'Cart.update',
({ data }) => {
@@ -30,9 +30,9 @@ const posModule = ({ api, protocol }) => {
const i = {
cart: {
async fetch() {
const abortController = new AbortController();
const o = U();
const o = subscribeAllErrors();
protocol.send('Cart.fetch', {
id: t,
});
@@ -197,7 +197,7 @@ const posModule = ({ api, protocol }) => {
protocol.send('Pos.close');
},
async device() {
const t = U();
const t = subscribeAllErrors();
protocol.subscribe(
'getState',
({ pos }) => {
@@ -215,7 +215,7 @@ const posModule = ({ api, protocol }) => {
return t.promise;
},
async location() {
const t = U();
const t = subscribeAllErrors();
protocol.subscribe(
'getState',
({ pos }) => {
modules/print.js +2 -2
@@ -4,10 +4,10 @@
*/
const printModule = ({ protocol, internalApiPromise }) => {
Mt(self, 'print', function () {
safeAsyncCall(self, 'print', function () {
const e = document.scrollingElement?.scrollHeight || document.body.offsetHeight;
CLICKABLE_TAGS(async () => {
parseUrl(async () => {
const { print: o } = (await internalApiPromise) || {};
if (o) {
await o({
modules/s-app-nav.js +1 -1
@@ -3,7 +3,7 @@
* Custom <s-app-nav> element for app navigation
*/
// Registry entry referenced as: An
// Registry entry referenced as: kn
modules/s-app-window.js +1 -1
@@ -3,9 +3,9 @@
* Custom <s-app-window> element for app window management
*/
// Registry entry referenced as: oe
// Registry entry referenced as: re
variantLock: 'app-window',
});
modules/share.js +1 -1
@@ -4,15 +4,15 @@
*/
const shareModule = ({ protocol, internalApiPromise }) => {
const e = navigator.share;
Mt(navigator, 'share', async function (o) {
safeAsyncCall(navigator, 'share', async function (o) {
if (!o) return e.call(navigator, o);
const { share: i } = (await internalApiPromise) || {};
const { title: r, text: a, url: s } = o;
if (!i)
return new Promise((n, e) => {
const abortController = new AbortController();
const { signal: c } = abortController;
function u(t) {
modules/sidekick.js +2 -2
@@ -112,15 +112,15 @@ const sidekickModule = ({ api, internalApiPromise, rpcEventTarget }) => {
function i() {
o = true;
e?.();
e = undefined;
}
CLICKABLE_TAGS(async () => {
parseUrl(async () => {
throw Error('Sidekick API is not available');
if (o) {
i();
}
@@ -133,15 +133,15 @@ const sidekickModule = ({ api, internalApiPromise, rpcEventTarget }) => {
function i() {
o = true;
e?.();
e = undefined;
}
CLICKABLE_TAGS(async () => {
parseUrl(async () => {
throw Error('Sidekick API is not available');
if (o) {
i();
}
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 = `${Wn}, ${Hn}, ${Vn}`;
const e = `${Vn}, ${zn}, ${Gn}`;
if (o) return void o.click();
const i = Array.from(document.querySelectorAll('s-menu, s-button-group'));
for (const n of i) {
if (e) return void e.click();
}
}
o?.click();
}
function a(t) {
modules/toast.js +2 -2
@@ -6,12 +6,12 @@
const toastModule = ({ api, protocol, internalApiPromise }) => {
api.toast = {
show(t, o = {}) {
CLICKABLE_TAGS(async () => {
parseUrl(async () => {
const { toast: r } = (await internalApiPromise) || {};
if (r?.show)
await r.show(t, {
...o,
id: i,
});
@@ -55,7 +55,7 @@ const toastModule = ({ api, protocol, internalApiPromise }) => {
return i;
},
hide(t) {
CLICKABLE_TAGS(async () => {
parseUrl(async () => {
const { toast: o } = (await internalApiPromise) || {};
if (o?.hide) {
await o.hide(t);
modules/tools.js +3 -3
@@ -8,7 +8,7 @@ const toolsModule = async ({ api, internalApiPromise }) => {
api.tools = {
register(t, i) {
const abortController = new AbortController();
CLICKABLE_TAGS(async () => {
parseUrl(async () => {
const { tools: o } = (await internalApiPromise) || {};
if (abortController.signal.aborted) return;
if (!o || typeof o.register != 'function') throw Error('Tools API is not available');
@@ -49,7 +49,7 @@ const toolsModule = async ({ api, internalApiPromise }) => {
o.add(abortController);
map.get(t)?.();
map.delete(t);
CLICKABLE_TAGS(async () => {
parseUrl(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();
o.forEach((t) => t.abort());
o.clear();
CLICKABLE_TAGS(async () => {
parseUrl(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: de
// Registry entry referenced as: he
modules/ui-nav-menu.js +1 -1
@@ -3,7 +3,7 @@
* Custom <ui-nav-menu> element
*/
// Registry entry referenced as: pe
// Registry entry referenced as: me