All entries

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.
20 files changed +95 -73

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