All entries

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.
20 files changed +66 -67

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