May 7, 2026
5fe1478780c8346832444e49f2f4e599094d3d06e9b709913a6533c81adde5e0 Previous:
b3f8e93d Bundle: 89.5 KB Intents module gains a reactive request signal and response now only exists when an intent is active.
Highlights
- The intents module now exposes api.intents.request as a reactive signal that tracks the current active intent value.
- The api.intents.response property is now a getter returning undefined when no intent is active, instead of always being defined.
- Intent request state is kept synchronized with the internal API via subscription and unsubscribed automatically on page unload.
- The intents module now accepts a signalFactory parameter, enabling reactive SignalLike signals for intent state management.
- The intents register method now uses a properly scoped AbortController local to each registration call.
Infrastructure Changes
REPORT.md +6 -6
@@ -1,14 +1,14 @@
# Shopify App Bridge — Unminification Report
Generated: 2026-04-30T08:43:10.147Z
Generated: 2026-05-07T08:51:17.772Z
## Files
| File | Size | Lines | Type |
|------|------|-------|------|
| _bootstrap.js | 31.8KB | 1119 | Infrastructure |
| _bootstrap.js | 29.2KB | 1013 | Infrastructure |
| _remote-ui.js | 6.0KB | 255 | Infrastructure |
| _utilities.js | 69.1KB | 2574 | Infrastructure |
| _utilities.js | 72.2KB | 2707 | Infrastructure |
| _web-vitals.js | 11.6KB | 527 | Infrastructure |
| analytics.js | 211B | 11 | Module |
| app.js | 346B | 15 | Module |
@@ -17,7 +17,7 @@ Generated: 2026-04-30T08:43:10.147Z
| fetch.js | 3.1KB | 94 | Module |
| id-token.js | 661B | 28 | Module |
| index.js | 2.2KB | 48 | Index |
| intents.js | 2.7KB | 89 | Module |
| intents.js | 3.1KB | 106 | Module |
| internal-only.js | 543B | 26 | Module |
| loading.js | 605B | 31 | Module |
| navigation.js | 416B | 19 | Module |
@@ -30,7 +30,7 @@ Generated: 2026-04-30T08:43:10.147Z
| s-app-nav.js | 175B | 10 | Module |
| s-app-window.js | 228B | 12 | Module |
| save-bar.js | 3.4KB | 138 | Module |
| scanner.js | 2.8KB | 101 | Module |
| scanner.js | 2.9KB | 101 | Module |
| scopes.js | 797B | 26 | Module |
| share.js | 1.3KB | 58 | Module |
| shopifyQL.js | 231B | 12 | Module |
@@ -46,7 +46,7 @@ Generated: 2026-04-30T08:43:10.147Z
| user.js | 940B | 37 | Module |
| visibility.js | 973B | 34 | Module |
| web-vitals.js | 1.8KB | 64 | Module |
| **Total** | **169.6KB** | **6449** | |
| **Total** | **170.7KB** | **6493** | |
## Pipeline Stages
modules/_bootstrap.js Truncated +3 -116
@@ -168,121 +168,7 @@
removeEventListener: globalThis.removeEventListener.bind(globalThis),
postMessage: globalThis.parent.postMessage.bind(globalThis.parent),
});
const c = (function (t = false) {
const c = interceptProperty();
let n = null;
let e = t ? null : new Set();
var i = checkPrivateField('value');
var o = checkPrivateField('callbacks');
class r {
constructor(t) {
var n;
Object.defineProperty(this, i, {
writable: true,
value: undefined,
});
Object.defineProperty(this, o, {
writable: true,
value: new Set(),
});
privateKeyCounter(this, i)[i] = t;
if (!((n = e) == null)) {
n.add(this);
}
}
get value() {
return privateKeyCounter(this, i)[i];
}
set value(t) {
if (t !== privateKeyCounter(this, i)[i]) {
privateKeyCounter(this, i)[i] = t;
privateKeyCounter(this, o)[o].forEach((n) => n(t));
}
}
subscribe(t) {
privateKeyCounter(this, o)[o].add(t);
return () => {
privateKeyCounter(this, o)[o].delete(t);
};
}
}
class a extends r {
constructor(t) {
var e;
super(t);
if (!((e = n) == null)) {
e.call(this, t);
}
}
get current() {
return this.value;
}
get value() {
return c().get.call(this);
}
set value(t) {
throw Error('This signal is read-only');
}
}
let s = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(a.prototype), 'value');
function c() {
if (!s || typeof s.get != 'function' || typeof s.set != 'function')
throw Error('Value descriptor is not found');
return s;
}
function u() {
e = null;
}
return {
SignalLike: a,
applyRealSignal: function (t) {
if (!e) return;
n = t;
const i = c();
Object.setPrototypeOf(a.prototype, t.prototype);
s = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(a.prototype), 'value');
Object.defineProperties(a.prototype, {
$$typeof: {
configurable: true,
value: t.prototype.$$typeof,
},
ref: {
configurable: true,
value: null,
},
constructor: {
configurable: true,
value: undefined,
},
type: {
configurable: true,
value: t.prototype.type,
},
props: {
configurable: true,
get() {
return {
data: this,
};
},
},
K: {
configurable: true,
value: 1,
},
});
if (e)
for (const n of e) {
const e = i.get.call(n);
t.call(n, e);
}
u();
},
sealImplementation: u,
updateSignalValue: function (t, n) {
c().set.call(t, n);
},
};
})();
const l = (function (t) {
const n = t.decodeSignal;
return (t) => {
@@ -581,11 +467,11 @@
})(n.get('shopify-reload'));
}
}
v.resolve(undefined);
return void (async function () {
const t = window.name.endsWith('/src');
const n = Vn(`frame:${L.apiKey}/main`) || Vn(S);
const n = Vn(`frame:${S.apiKey}/main`) || Vn(C);
if (n) {
window.opener = n;
window.fetch = n.fetch;
@@ -714,6 +600,7 @@
);
const i = (function (t) {
const n = {};
let e = false;
return {
async set(e) {
const i = await t;
Diff truncated at 200 lines
modules/_utilities.js Truncated +48 -3
@@ -101,12 +101,12 @@ const R = [
'UnexpectedAction',
'UnsupportedOperation',
];
function $(t, n, e) {
function subscribeAllErrors(t, n, e) {
R.forEach((i) => {
t.subscribe('Error.' + i, n, e);
});
}
let t;
let n = false;
const promise = new Promise((n) => {
@@ -125,7 +125,7 @@ function _() {
},
};
}
function subscribeAllErrors() {
function _() {
let t = Promise.resolve();
const n = {};
return {
@@ -567,10 +567,126 @@ var createPrivateKey = 0;
function checkPrivateField(t) {
return `__private_${createPrivateKey++}_${t}`;
}
const interceptProperty = Symbol();
function interceptProperty(t = false) {
let n = null;
let e = t ? null : new Set();
var i = checkPrivateField('value');
var o = checkPrivateField('callbacks');
class r {
constructor(t) {
var n;
Object.defineProperty(this, i, {
writable: true,
value: undefined,
});
Object.defineProperty(this, o, {
writable: true,
value: new Set(),
});
privateKeyCounter(this, i)[i] = t;
if (!((n = e) == null)) {
n.add(this);
}
}
get value() {
return privateKeyCounter(this, i)[i];
}
set value(t) {
if (t !== privateKeyCounter(this, i)[i]) {
privateKeyCounter(this, i)[i] = t;
privateKeyCounter(this, o)[o].forEach((n) => n(t));
}
}
subscribe(t) {
privateKeyCounter(this, o)[o].add(t);
return () => {
privateKeyCounter(this, o)[o].delete(t);
};
}
}
class a extends r {
constructor(t) {
var e;
super(t);
if (!((e = n) == null)) {
e.call(this, t);
}
}
get current() {
... (truncated)
Diff truncated at 200 lines
modules/_web-vitals.js Truncated +10 -10
@@ -65,42 +65,42 @@ var be = function (t) {
}
};
};
return document.visibilityState !== 'hidden' || document.prerendering ? 1 / 0 : 0;
};
}
};
var Ae = function () {
addEventListener('visibilitychange', ge, true);
addEventListener('prerenderingchange', ge, true);
};
var Ee = function () {
removeEventListener('visibilitychange', ge, true);
addEventListener('visibilitychange', Ae, true);
removeEventListener('prerenderingchange', ge, true);
addEventListener('prerenderingchange', Ae, true);
};
var ke = function () {
if (ye < 0) {
removeEventListener('visibilitychange', Ae, true);
ye = ve();
removeEventListener('prerenderingchange', Ae, true);
Ae();
};
ue(function () {
var Pe = function () {
if (ve < 0) {
ve = ge();
Ee();
le(function () {
setTimeout(function () {
}, 0);
});
}
return {
get firstHiddenTime() {
},
};
};
if (document.prerendering) {
addEventListener(
'prerenderingchange',
Diff truncated at 200 lines
Module Changes
modules/fetch.js +3 -3
@@ -3,8 +3,8 @@
* Intercepted fetch with auth headers and session token refresh
*/
// Registry entry referenced as: It
// Registry entry referenced as: Mt
const i = self.fetch;
async function o(t, n) {
const e = new Headers(n.headers).get('Shopify-Challenge-Required');
@@ -63,16 +63,16 @@ const It = ({ api, protocol, internalApiPromise }) => {
const w = request.clone();
let b;
if (p?.fetch) {
const t = await Tt(request);
const t = await restoreProperty(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 Tt(w);
const t = await restoreProperty(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/intents.js +47 -30
@@ -3,7 +3,7 @@
* Intent-based navigation and deep linking
*/
const intentsModule = ({ api, protocol, internalApiPromise }) => {
const intentsModule = ({ api, protocol, internalApiPromise, signalFactory }) => {
api.data.intent = (function () {
try {
const t = new URLSearchParams(location.search).get('intent');
@@ -21,17 +21,30 @@ const intentsModule = ({ api, protocol, internalApiPromise }) => {
console.error(err);
}
})();
const abortController = new AbortController();
const o = new signalFactory.SignalLike(api.data.intent ?? null);
let r;
async function a() {
const t = (await internalApiPromise) || {};
if (
o.value == null ||
typeof t.intents?.response?.ok != 'function' ||
typeof t.intents?.response?.error != 'function' ||
typeof t.intents?.response?.closed != 'function'
)
throw Error('Cannot respond to intent in this context');
return t.intents.response;
}
api.intents = {
request: o,
register(t) {
const e = new URLSearchParams(location.search).get('step_reference') || '';
const i = new AbortController();
const abortController = new AbortController();
protocol.send('AppFrame.requestProperties');
protocol.subscribe(
'AppFrame.propertiesEvent',
({ properties }) => {
const o = (function (t, n, e) {
return new xt('configure', 'gid://flow/stepReference/' + t, n, () =>
return new safeAsyncCall('configure', 'gid://flow/stepReference/' + t, n, () =>
e.send('AppFrame.navigateBack'),
);
})(
@@ -44,45 +57,49 @@ const intentsModule = ({ api, protocol, internalApiPromise }) => {
t(o);
},
{
signal: i.signal,
signal: abortController.signal,
},
);
return () => i.abort();
return () => abortController.abort();
},
async invoke(t, n) {
const i = await 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 safeAsyncCall(i.intents.invoke(t, n));
return new WINDOW_TARGETS(i.intents.invoke(t, n));
},
};
if (ot()) {
internalApiPromise.then((t) => {
async function o() {
const n = t?.intents?.request;
const t = (await internalApiPromise) || {};
if (n && typeof n.subscribe == 'function') {
if (
signalFactory.updateSignalValue(o, n.value ?? null);
typeof t.intents?.response?.ok != 'function' ||
r?.();
typeof t.intents?.response?.error != 'function' ||
r = n.subscribe((t) => {
typeof t.intents?.response?.closed != 'function'
signalFactory.updateSignalValue(o, t ?? null);
)
});
throw Error('Cannot respond to intent in this context');
if (abortController.signal.aborted) throw Error('Cannot resolve an intent multiple times');
abortController.abort();
return t.intents.response;
}
api.intents.response = {
});
async ok(t) {
globalThis.addEventListener?.('beforeunload', () => r?.(), {
const n = await o();
once: true,
return await n.ok(t);
});
},
const s = {
async error(t, n) {
async ok(t) {
return await e.error(t, n);
return await n.ok(t);
async closed() {
async error(t, n) {
return await t.closed();
return await e.error(t, n);
};
async closed() {
}
const t = await a();
return await t.closed();
},
};
Object.defineProperty(api.intents, 'response', {
configurable: true,
enumerable: true,
get: () => (o.value == null ? undefined : s),
});
};
modules/resource-picker.js +1 -1
@@ -80,7 +80,7 @@ const resourcePickerModule = ({ api, protocol, internalApiPromise }) => {
signal: p,
},
);
$(
subscribeAllErrors(
protocol,
(t) => {
m();
modules/s-app-nav.js +1 -1
@@ -3,7 +3,7 @@
* Custom <s-app-nav> element for app navigation
*/
// Registry entry referenced as: hn
// Registry entry referenced as: pn
modules/s-app-window.js +1 -1
@@ -3,9 +3,9 @@
* Custom <s-app-window> element for app window management
*/
// Registry entry referenced as: Hn
// Registry entry referenced as: Kn
variantLock: 'app-window',
});
modules/scanner.js +2 -2
@@ -22,7 +22,7 @@ const scannerModule = ({ api, protocol, internalApiPromise }) => {
);
}
function s() {
$(protocol, a, {
subscribeAllErrors(protocol, a, {
signal: r,
id: i,
});
@@ -50,14 +50,14 @@ const scannerModule = ({ api, protocol, internalApiPromise }) => {
protocol.subscribe(
'getState',
({ features }) => {
s();
} else {
(function () {
const e = new AbortController();
abortController.signal.addEventListener('abort', () => e.abort());
$(
subscribeAllErrors(
protocol,
(t) => {
e.abort();
modules/share.js +1 -1
@@ -23,7 +23,7 @@ const shareModule = ({ protocol, internalApiPromise }) => {
}),
);
}
$(protocol, u, {
subscribeAllErrors(protocol, u, {
signal: c,
id: 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 = `${Rn}, ${$n}, ${_n}`;
const e = `${Fn}, ${$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/ui-modal.js +1 -1
@@ -3,7 +3,7 @@
* Custom <ui-modal> element
*/
// Registry entry referenced as: Zn
// Registry entry referenced as: te
modules/ui-nav-menu.js +1 -1
@@ -3,7 +3,7 @@
* Custom <ui-nav-menu> element
*/
// Registry entry referenced as: te
// Registry entry referenced as: ne