April 22, 2026
e35faefdd590e1d8830c09cbf8efb30cf433c01a50a799b3a0ad0f96752f56d7 Previous:
07f68577 Bundle: 88.7 KB Several modules broadened protocol support from Shopify-only to all protocols, with minor error tracking improvements.
Highlights
- toast.show and toast.hide switched from SHOPIFY_PROTOCOLS to ALL_PROTOCOLS, enabling them to work across all protocol environments.
- sidekick.registerToolHandler and sidekick.registerContextCallback switched from SHOPIFY_PROTOCOLS to ALL_PROTOCOLS.
- tools.register, tools.unregister, and tools.clear switched from SHOPIFY_PROTOCOLS to ALL_PROTOCOLS.
- The print module's window.print interceptor switched from SHOPIFY_PROTOCOLS to ALL_PROTOCOLS.
- RemoteUI MissingResolverError now includes a groupingHash property set to RemoteUI::MissingResolverError for improved error grouping.
Infrastructure Changes
REPORT.md +9 -9
@@ -1,30 +1,30 @@
# Shopify App Bridge — Unminification Report
Generated: 2026-04-11T08:10:05.932Z
Generated: 2026-04-22T08:31:20.292Z
## Files
| File | Size | Lines | Type |
|------|------|-------|------|
| _bootstrap.js | 31.9KB | 1122 | Infrastructure |
| _bootstrap.js | 31.8KB | 1119 | Infrastructure |
| _remote-ui.js | 6.0KB | 254 | Infrastructure |
| _remote-ui.js | 6.0KB | 255 | Infrastructure |
| _utilities.js | 69.2KB | 2571 | Infrastructure |
| _utilities.js | 69.2KB | 2574 | 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.1KB | 94 | Module |
| id-token.js | 640B | 28 | Module |
| id-token.js | 661B | 28 | Module |
| index.js | 2.2KB | 48 | Index |
| intents.js | 2.7KB | 89 | Module |
| internal-only.js | 539B | 26 | Module |
| internal-only.js | 543B | 26 | Module |
| loading.js | 605B | 31 | Module |
| navigation.js | 416B | 19 | Module |
| picker.js | 451B | 18 | Module |
| polaris.js | 240B | 12 | Module |
| pos.js | 5.7KB | 248 | Module |
| print.js | 575B | 25 | Module |
| print.js | 569B | 25 | Module |
| resource-picker.js | 2.8KB | 119 | Module |
| reviews.js | 365B | 16 | Module |
| s-app-nav.js | 175B | 10 | Module |
@@ -34,7 +34,7 @@ Generated: 2026-04-11T08:10:05.932Z
| scopes.js | 797B | 26 | Module |
| share.js | 1.3KB | 58 | Module |
| shopifyQL.js | 231B | 12 | Module |
| shortcut.js | 478B | 24 | Module |
| shortcut.js | 461B | 24 | Module |
| sidekick.js | 4.4KB | 147 | Module |
| support.js | 508B | 21 | Module |
| telemetry.js | 813B | 30 | Module |
@@ -46,7 +46,7 @@ Generated: 2026-04-11T08:10:05.932Z
| user.js | 940B | 37 | Module |
| visibility.js | 973B | 34 | Module |
| web-vitals.js | 1.8KB | 64 | Module |
| **Total** | **169.8KB** | **6448** | |
| **Total** | **169.7KB** | **6449** | |
## Pipeline Stages
modules/_bootstrap.js Truncated +11 -16
@@ -88,42 +88,38 @@
if (i.src)
try {
const url = new URL(i.src);
url.searchParams.forEach((t, e) => {
if (t) {
n[e] = t;
}
});
}
} catch (err) {}
else if (i.type === 'shopify/config')
try {
} catch (err) {
console.warn('App Bridge Next: failed to parse configuration. ' + err);
}
return n;
})(),
);
n,
(function () {
const t = Array.from(document.querySelectorAll('meta[name^="shopify-"i]'));
const n = {};
for (const e of t) {
if (!e.hasAttribute('name')) continue;
const t = e
const t = m(e.getAttribute('name').replace(/shopify-/i, ''));
.getAttribute('name')
n[t] = dt(t, e.getAttribute('content') ?? undefined);
.replace(/shopify-/i, '')
.toLowerCase()
.replace(/-+(.)/g, (t, n) => n.toUpperCase());
n[t] = lt(t, e.getAttribute('content') ?? undefined);
}
return n;
})(),
);
n,
(function (t) {
return {
@@ -175,8 +171,8 @@
const c = (function (t = false) {
let n = null;
let e = t ? null : new Set();
var i = privateKeyCounter('value');
var i = createPrivateKey('value');
var o = privateKeyCounter('callbacks');
var o = createPrivateKey('callbacks');
class r {
constructor(t) {
var n;
@@ -188,24 +184,24 @@
writable: true,
value: new Set(),
});
createPrivateKey(this, i)[i] = t;
checkPrivateField(this, i)[i] = t;
if (!((n = e) == null)) {
n.add(this);
}
}
get value() {
return createPrivateKey(this, i)[i];
return checkPrivateField(this, i)[i];
}
set value(t) {
if (t !== createPrivateKey(this, i)[i]) {
if (t !== checkPrivateField(this, i)[i]) {
createPrivateKey(this, i)[i] = t;
checkPrivateField(this, i)[i] = t;
createPrivateKey(this, o)[o].forEach((n) => n(t));
checkPrivateField(this, o)[o].forEach((n) => n(t));
}
}
subscribe(t) {
createPrivateKey(this, o)[o].add(t);
checkPrivateField(this, o)[o].add(t);
return () => {
createPrivateKey(this, o)[o].delete(t);
checkPrivateField(this, o)[o].delete(t);
};
}
}
@@ -455,7 +451,7 @@
return {
send: function (t, n) {
if (t !== 'getState') {
o('dispatch', pt(t, n));
... (truncated)
Diff truncated at 200 lines
modules/_remote-ui.js +1 -0
@@ -245,6 +245,7 @@ class f extends Error {
this.callId = undefined;
this.error = undefined;
this.result = undefined;
this.groupingHash = 'RemoteUI::MissingResolverError';
this.name = 'MissingResolverError';
this.callId = n;
this.error = e;
modules/_utilities.js Truncated +18 -15
@@ -10,7 +10,10 @@ function h() {
function p() {
return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16);
}
function m(t, n) {
function m(t) {
return t.toLowerCase().replace(/-+(.)/g, (t, n) => n.toUpperCase());
}
function w(t, n) {
if (n)
for (let e in n) {
const i = n[e];
@@ -33,41 +36,41 @@ class y 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 A() {
return g('Unframed') && 'MobileWebView' in window;
}
function E() {
return g('Shopify Mobile');
return A('Unframed') && 'MobileWebView' in window;
}
function k() {
return g('Extensibility');
return A('Shopify Mobile');
}
function P() {
return g('Shopify POS');
return A('Extensibility');
}
function C() {
return A('Shopify POS');
}
return {
apiKey: t,
scope: n,
mode: e,
};
})();
'hmac',
'locale',
'protocol',
@@ -122,7 +125,7 @@ function $() {
},
};
}
function _() {
function subscribeAllErrors() {
let t = Promise.resolve();
const n = {};
return {
@@ -148,7 +151,7 @@ function _() {
},
};
}
function subscribeAllErrors({ keys, held, handler, keyEvent: i = 'keydown' }) {
function U({ keys, held, handler, keyEvent: i = 'keydown' }) {
let o = [];
const r = (i) => {
if (keys.flat().includes(i.key)) {
@@ -194,25 +197,25 @@ function createDeferred() {
const t = window.shopify.config.host;
return 'https://' + atob(t);
}
const U = Symbol();
const N = Symbol();
const j = Symbol();
const D = Symbol();
const q = 'data-save-bar';
const q = Symbol();
const W = 'data-discard-confirmation';
const W = 'data-save-bar';
const V = 'ui-save-bar';
const V = 'data-discard-confirmation';
const z = 'update';
const z = 'ui-save-bar';
function G(t, { onChange, filter: e = () => true }) {
const G = 'update';
function H(t, { onChange, filter: e = () => true }) {
function i() {
let o = false;
let r = false;
for (const t of i) {
if (o) break;
}
for (const t of i) {
if (r) break;
}
const a = o
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: Lt
// Registry entry referenced as: It
const i = self.fetch;
async function o(t, n) {
const e = new Headers(n.headers).get('Shopify-Challenge-Required');
@@ -16,7 +16,7 @@ const Lt = ({ api, protocol, internalApiPromise }) => {
verified: false,
};
}
interceptProperty(globalThis, 'fetch', async function (r, a) {
ORIGINAL_SYMBOL(globalThis, 'fetch', async function (r, a) {
const request = new Request(r instanceof Request ? r.clone() : r, a);
const { appOrigins: c = [] } = api.config;
const url = new URL(request.url);
@@ -63,16 +63,16 @@ const Lt = ({ api, protocol, internalApiPromise }) => {
const w = request.clone();
let b;
if (p?.fetch) {
const t = await restoreProperty(request);
const t = await Tt(request);
const n = await p.fetch(t);
} else b = await i(request);
if (b.headers.get('X-Shopify-Retry-Invalid-Session-Request') && m) {
w.headers.set('Authorization', 'Bearer ' + (await api.idToken()));
if (p?.fetch) {
const t = await restoreProperty(w);
const t = await Tt(w);
const n = await p.fetch(t);
} else b = await i(w);
}
const y = b.headers.get('X-Shopify-API-Request-Failure-Reauthorize-Url');
modules/id-token.js +3 -3
@@ -3,8 +3,8 @@
* Session token (ID token) request/response
*/
// Registry entry referenced as: It
// Registry entry referenced as: deepClone
const It = ({ api, protocol, internalApiPromise }) => {
const deepClone = ({ api, protocol, internalApiPromise }) => {
api.idToken = async function () {
const { idToken: t } = (await internalApiPromise) || {};
return t
@@ -24,4 +24,4 @@ const It = ({ api, protocol, internalApiPromise }) => {
};
};
const idTokenModule = It;
const idTokenModule = deepClone;
modules/intents.js +1 -1
@@ -54,10 +54,10 @@ const intentsModule = ({ api, protocol, internalApiPromise }) => {
if (!i) throw Error('Cannot invoke intent');
if (!i.intents?.invoke || typeof i.intents.invoke != 'function')
throw Error('Intents are not supported');
return new xt(i.intents.invoke(t, n));
return new safeAsyncCall(i.intents.invoke(t, n));
},
};
async function o() {
const t = (await internalApiPromise) || {};
if (
modules/internal-only.js +1 -1
@@ -6,7 +6,7 @@
const internalOnlyModule = ({ api, internalApiPromise }) => {
const e = {
async show(t, e) {
const i = safeAsyncCall(e);
const i = SHOPIFY_PROTOCOLS(e);
const o = await internalApiPromise;
if (o && o.internalModal) {
await o.internalModal.show?.(t, i);
modules/navigation.js +2 -2
@@ -4,13 +4,13 @@
*/
const navigationModule = (t) => {
const n = generateId(t);
const n = Ht(t);
t.internalApiPromise.then((e) => {
const { navigation: i } = e || {};
if (i?.version === 2)
try {
n();
zt(t);
generateId(t);
} catch (err) {
console.error('Failed to set up navigation: ' + err);
}
modules/print.js +2 -2
@@ -4,10 +4,10 @@
*/
const printModule = ({ protocol, internalApiPromise }) => {
interceptProperty(self, 'print', function () {
ORIGINAL_SYMBOL(self, 'print', function () {
const e = document.scrollingElement?.scrollHeight || document.body.offsetHeight;
SHOPIFY_PROTOCOLS(async () => {
ALL_PROTOCOLS(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: fn
// Registry entry referenced as: hn
modules/s-app-window.js +1 -1
@@ -3,9 +3,9 @@
* Custom <s-app-window> element for app window management
*/
// Registry entry referenced as: Vn
// Registry entry referenced as: Hn
variantLock: 'app-window',
});
modules/share.js +1 -1
@@ -4,15 +4,15 @@
*/
const shareModule = ({ protocol, internalApiPromise }) => {
const e = navigator.share;
interceptProperty(navigator, 'share', async function (i) {
ORIGINAL_SYMBOL(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 }) => {
subscribeAllErrors({
U({
...e,
handler: async () => {
const { shortcut: i } = (await internalApiPromise) || {};
modules/sidekick.js +2 -2
@@ -108,7 +108,7 @@ const sidekickModule = ({ api, internalApiPromise, rpcEventTarget }) => {
s(e);
e = undefined;
}
SHOPIFY_PROTOCOLS(async () => {
ALL_PROTOCOLS(async () => {
const { sidekick: a } = (await internalApiPromise) || {};
if (!a || typeof a.registerToolHandler != 'function')
throw Error('Sidekick API is not available');
@@ -129,7 +129,7 @@ const sidekickModule = ({ api, internalApiPromise, rpcEventTarget }) => {
s(e);
e = undefined;
}
SHOPIFY_PROTOCOLS(async () => {
ALL_PROTOCOLS(async () => {
const { sidekick: a } = (await internalApiPromise) || {};
if (!a || typeof a.registerContextCallback != 'function')
throw Error('Sidekick API is not available');
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 = `${On}, ${Mn}, ${xn}`;
const e = `${Rn}, ${$n}, ${_n}`;
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/toast.js +2 -2
@@ -6,12 +6,12 @@
const toastModule = ({ api, protocol, internalApiPromise }) => {
api.toast = {
show(t, i = {}) {
SHOPIFY_PROTOCOLS(async () => {
ALL_PROTOCOLS(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) {
SHOPIFY_PROTOCOLS(async () => {
ALL_PROTOCOLS(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();
SHOPIFY_PROTOCOLS(async () => {
ALL_PROTOCOLS(async () => {
const { tools: i } = (await internalApiPromise) || {};
if (abortController.signal.aborted) return;
if (!i || typeof i.register != 'function') throw Error('Tools API is not available');
@@ -31,7 +31,7 @@ const toolsModule = async ({ api, internalApiPromise }) => {
i.add(abortController);
map.get(t)?.();
map.delete(t);
SHOPIFY_PROTOCOLS(async () => {
ALL_PROTOCOLS(async () => {
const { tools: e } = (await internalApiPromise) || {};
if (!abortController.signal.aborted) {
if (!e || typeof e.unregister != 'function') throw Error('Tools API is not available');
@@ -46,7 +46,7 @@ const toolsModule = async ({ api, internalApiPromise }) => {
map.clear();
i.forEach((t) => t.abort());
i.clear();
SHOPIFY_PROTOCOLS(async () => {
ALL_PROTOCOLS(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: Jn
// Registry entry referenced as: Zn
modules/ui-nav-menu.js +1 -1
@@ -3,7 +3,7 @@
* Custom <ui-nav-menu> element
*/
// Registry entry referenced as: Yn
// Registry entry referenced as: te