diff --git a/.gitattributes b/.gitattributes
index ce6b1cdc..f6db3d54 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -19,3 +19,6 @@
/infection.json5 export-ignore
/.gitignore export-ignore
/.gitattributes export-ignore
+
+# Vendored/minified frontend libraries: treat as binary to keep diffs readable
+src/view/frontend/web/js/lib/alpine.min.js binary
diff --git a/.github/workflows/functional-tests.yml b/.github/workflows/functional-tests.yml
index 205c67b7..a8e51ff5 100644
--- a/.github/workflows/functional-tests.yml
+++ b/.github/workflows/functional-tests.yml
@@ -200,10 +200,10 @@ jobs:
# Verify Alpine.js auto-loading in template
echo "Verifying Alpine.js auto-loading for non-Hyvä themes:"
- if grep -q "jsdelivr.net/npm/alpinejs" "$INSPECTOR_TPL"; then
- echo "✓ Template includes Alpine.js CDN loading"
+ if grep -q "getAlpineJsUrl" "$INSPECTOR_TPL"; then
+ echo "✓ Template includes local Alpine.js fallback loading"
else
- echo "✗ Template missing Alpine.js auto-loading"
+ echo "✗ Template missing local Alpine.js fallback loading"
exit 1
fi
@@ -214,6 +214,13 @@ jobs:
exit 1
fi
+ if grep -q "jsdelivr.net/npm/alpinejs" "$INSPECTOR_TPL"; then
+ echo "✗ Template still references external Alpine.js CDN"
+ exit 1
+ else
+ echo "✓ Template no longer relies on external Alpine.js CDN"
+ fi
+
# Test Luma theme (non-Hyvä)
echo "Testing Inspector with Luma theme (non-Hyvä):"
bin/magento config:set design/theme/theme_id 4 # Luma theme ID
diff --git a/src/Block/Inspector.php b/src/Block/Inspector.php
index 7af90343..f2ef3306 100644
--- a/src/Block/Inspector.php
+++ b/src/Block/Inspector.php
@@ -101,6 +101,16 @@ public function getToolbarJsUrl(): string
return $this->getViewFileUrl('OpenForgeProject_MageForge::js/toolbar.js');
}
+ /**
+ * Get local Alpine.js fallback URL
+ *
+ * @return string
+ */
+ public function getAlpineJsUrl(): string
+ {
+ return $this->getViewFileUrl('OpenForgeProject_MageForge::js/lib/alpine.min.js');
+ }
+
/**
* Whether button labels should be displayed in the toolbar
*
diff --git a/src/etc/frontend/csp_whitelist.xml b/src/etc/frontend/csp_whitelist.xml
deleted file mode 100644
index eb3a94f1..00000000
--- a/src/etc/frontend/csp_whitelist.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
- cdn.jsdelivr.net
-
-
-
-
diff --git a/src/view/frontend/templates/inspector.phtml b/src/view/frontend/templates/inspector.phtml
index f7906bbc..532376a5 100644
--- a/src/view/frontend/templates/inspector.phtml
+++ b/src/view/frontend/templates/inspector.phtml
@@ -8,7 +8,8 @@ declare(strict_types=1);
* Initializes the Alpine.js inspector component.
* The floating button and info badge are created dynamically via JavaScript.
*
- * Note: Loads Alpine.js from CDN if not already available (for non-Hyvä themes like Luma)
+ * Note: Loads bundled Alpine.js locally as fallback if not already available
+ * (e.g. for non-Hyvä themes like Luma). No external CDN is used.
*
* @var \OpenForgeProject\MageForge\Block\Inspector $block
*/
@@ -17,31 +18,30 @@ declare(strict_types=1);
-
+
getAlpineJsUrl(), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP);
$alpineBootstrap = << {
+ var ee = !1,
+ re = !1,
+ W = [],
+ ne = -1,
+ ie = !1;
+ function Ve(t) {
+ Dn(t);
+ }
+ function Ue() {
+ ie = !0;
+ }
+ function qe() {
+ ((ie = !1), We());
+ }
+ function Dn(t) {
+ (W.includes(t) || W.push(t), We());
+ }
+ function Ke(t) {
+ let e = W.indexOf(t);
+ e !== -1 && e > ne && W.splice(e, 1);
+ }
+ function We() {
+ if (!re && !ee) {
+ if (ie) return;
+ ((ee = !0), queueMicrotask(In));
+ }
+ }
+ function In() {
+ ((ee = !1), (re = !0));
+ for (let t = 0; t < W.length; t++) (W[t](), (ne = t));
+ ((W.length = 0), (ne = -1), (re = !1));
+ }
+ var C,
+ R,
+ j,
+ se,
+ oe = !0;
+ function Ge(t) {
+ ((oe = !1), t(), (oe = !0));
+ }
+ function Je(t) {
+ ((C = t.reactive),
+ (j = t.release),
+ (R = (e) =>
+ t.effect(e, {
+ scheduler: (r) => {
+ oe ? Ve(r) : r();
+ },
+ })),
+ (se = t.raw));
+ }
+ function ae(t) {
+ R = t;
+ }
+ function Ye(t) {
+ let e = () => {};
+ return [
+ (n) => {
+ let i = R(n);
+ return (
+ t._x_effects ||
+ ((t._x_effects = new Set()),
+ (t._x_runEffects = () => {
+ t._x_effects.forEach((o) => o());
+ })),
+ t._x_effects.add(i),
+ (e = () => {
+ i !== void 0 && (t._x_effects.delete(i), j(i));
+ }),
+ i
+ );
+ },
+ () => {
+ e();
+ },
+ ];
+ }
+ function St(t, e) {
+ let r = !0,
+ n,
+ i,
+ o = R(() => {
+ let s = t(),
+ a = JSON.stringify(s);
+ if (!r && (typeof s == "object" || s !== n)) {
+ let c = typeof n == "object" ? JSON.parse(i) : n;
+ queueMicrotask(() => {
+ e(s, c);
+ });
+ }
+ ((n = s), (i = a), (r = !1));
+ });
+ return () => j(o);
+ }
+ async function Xe(t) {
+ Ue();
+ try {
+ (await t(), await Promise.resolve());
+ } finally {
+ qe();
+ }
+ }
+ var Ze = [],
+ Qe = [],
+ tr = [];
+ function er(t) {
+ tr.push(t);
+ }
+ function et(t, e) {
+ typeof e == "function"
+ ? (t._x_cleanups || (t._x_cleanups = []), t._x_cleanups.push(e))
+ : ((e = t), Qe.push(e));
+ }
+ function At(t) {
+ Ze.push(t);
+ }
+ function Ot(t, e, r) {
+ (t._x_attributeCleanups || (t._x_attributeCleanups = {}),
+ t._x_attributeCleanups[e] || (t._x_attributeCleanups[e] = []),
+ t._x_attributeCleanups[e].push(r));
+ }
+ function ce(t, e) {
+ t._x_attributeCleanups &&
+ Object.entries(t._x_attributeCleanups).forEach(([r, n]) => {
+ (e === void 0 || e.includes(r)) &&
+ (n.forEach((i) => i()), delete t._x_attributeCleanups[r]);
+ });
+ }
+ function rr(t) {
+ for (t._x_effects?.forEach(Ke); t._x_cleanups?.length;)
+ t._x_cleanups.pop()();
+ }
+ var le = new MutationObserver(pe),
+ ue = !1;
+ function ut() {
+ (le.observe(document, {
+ subtree: !0,
+ childList: !0,
+ attributes: !0,
+ attributeOldValue: !0,
+ }),
+ (ue = !0));
+ }
+ function fe() {
+ (kn(), le.disconnect(), (ue = !1));
+ }
+ var lt = [];
+ function kn() {
+ let t = le.takeRecords();
+ lt.push(() => t.length > 0 && pe(t));
+ let e = lt.length;
+ queueMicrotask(() => {
+ if (lt.length === e) for (; lt.length > 0;) lt.shift()();
+ });
+ }
+ function m(t) {
+ if (!ue) return t();
+ fe();
+ let e = t();
+ return (ut(), e);
+ }
+ var de = !1,
+ vt = [];
+ function nr() {
+ de = !0;
+ }
+ function ir() {
+ ((de = !1), pe(vt), (vt = []));
+ }
+ function pe(t) {
+ if (de) {
+ vt = vt.concat(t);
+ return;
+ }
+ let e = [],
+ r = new Set(),
+ n = new Map(),
+ i = new Map();
+ for (let o = 0; o < t.length; o++)
+ if (
+ !t[o].target._x_ignoreMutationObserver &&
+ (t[o].type === "childList" &&
+ (t[o].removedNodes.forEach((s) => {
+ s.nodeType === 1 && s._x_marker && r.add(s);
+ }),
+ t[o].addedNodes.forEach((s) => {
+ if (s.nodeType === 1) {
+ if (r.has(s)) {
+ r.delete(s);
+ return;
+ }
+ s._x_marker || e.push(s);
+ }
+ })),
+ t[o].type === "attributes")
+ ) {
+ let s = t[o].target,
+ a = t[o].attributeName,
+ c = t[o].oldValue,
+ l = () => {
+ (n.has(s) || n.set(s, []),
+ n.get(s).push({ name: a, value: s.getAttribute(a) }));
+ },
+ u = () => {
+ (i.has(s) || i.set(s, []), i.get(s).push(a));
+ };
+ s.hasAttribute(a) && c === null
+ ? l()
+ : s.hasAttribute(a)
+ ? (u(), l())
+ : u();
+ }
+ (i.forEach((o, s) => {
+ ce(s, o);
+ }),
+ n.forEach((o, s) => {
+ Ze.forEach((a) => a(s, o));
+ }));
+ for (let o of r) e.some((s) => s.contains(o)) || Qe.forEach((s) => s(o));
+ for (let o of e) o.isConnected && tr.forEach((s) => s(o));
+ ((e = null), (r = null), (n = null), (i = null));
+ }
+ function Ct(t) {
+ return P(F(t));
+ }
+ function N(t, e, r) {
+ return (
+ (t._x_dataStack = [e, ...F(r || t)]),
+ () => {
+ t._x_dataStack = t._x_dataStack.filter((n) => n !== e);
+ }
+ );
+ }
+ function F(t) {
+ return t._x_dataStack
+ ? t._x_dataStack
+ : typeof ShadowRoot == "function" && t instanceof ShadowRoot
+ ? F(t.host)
+ : t.parentNode
+ ? F(t.parentNode)
+ : [];
+ }
+ function P(t) {
+ return new Proxy({ objects: t }, $n);
+ }
+ function or(t, e) {
+ return t === null || t === Object.prototype
+ ? null
+ : Object.prototype.hasOwnProperty.call(t, e)
+ ? t
+ : or(Object.getPrototypeOf(t), e);
+ }
+ var $n = {
+ ownKeys({ objects: t }) {
+ return Array.from(new Set(t.flatMap((e) => Object.keys(e))));
+ },
+ has({ objects: t }, e) {
+ return e == Symbol.unscopables
+ ? !1
+ : t.some(
+ (r) =>
+ Object.prototype.hasOwnProperty.call(r, e) || Reflect.has(r, e),
+ );
+ },
+ get({ objects: t }, e, r) {
+ return e == "toJSON"
+ ? Ln
+ : Reflect.get(t.find((n) => Reflect.has(n, e)) || {}, e, r);
+ },
+ set({ objects: t }, e, r, n) {
+ let i;
+ for (let s of t) if (((i = or(s, e)), i)) break;
+ i || (i = t[t.length - 1]);
+ let o = Object.getOwnPropertyDescriptor(i, e);
+ return o?.set && o?.get ? o.set.call(n, r) || !0 : Reflect.set(i, e, r);
+ },
+ };
+ function Ln() {
+ return Reflect.ownKeys(this).reduce(
+ (e, r) => ((e[r] = Reflect.get(this, r)), e),
+ {},
+ );
+ }
+ function rt(t) {
+ let e = (n) => typeof n == "object" && !Array.isArray(n) && n !== null,
+ r = (n, i = "") => {
+ Object.entries(Object.getOwnPropertyDescriptors(n)).forEach(
+ ([o, { value: s, enumerable: a }]) => {
+ if (
+ a === !1 ||
+ s === void 0 ||
+ (typeof s == "object" && s !== null && s.__v_skip)
+ )
+ return;
+ let c = i === "" ? o : `${i}.${o}`;
+ typeof s == "object" && s !== null && s._x_interceptor
+ ? (n[o] = s.initialize(t, c, o))
+ : e(s) && s !== n && !(s instanceof Element) && r(s, c);
+ },
+ );
+ };
+ return r(t);
+ }
+ function Tt(t, e = () => {}) {
+ let r = {
+ initialValue: void 0,
+ _x_interceptor: !0,
+ initialize(n, i, o) {
+ return t(
+ this.initialValue,
+ () => jn(n, i),
+ (s) => me(n, i, s),
+ i,
+ o,
+ );
+ },
+ };
+ return (
+ e(r),
+ (n) => {
+ if (typeof n == "object" && n !== null && n._x_interceptor) {
+ let i = r.initialize.bind(r);
+ r.initialize = (o, s, a) => {
+ let c = n.initialize(o, s, a);
+ return ((r.initialValue = c), i(o, s, a));
+ };
+ } else r.initialValue = n;
+ return r;
+ }
+ );
+ }
+ function jn(t, e) {
+ return e.split(".").reduce((r, n) => r[n], t);
+ }
+ function me(t, e, r) {
+ if ((typeof e == "string" && (e = e.split(".")), e.length === 1))
+ t[e[0]] = r;
+ else {
+ if (e.length === 0) throw error;
+ return (t[e[0]] || (t[e[0]] = {}), me(t[e[0]], e.slice(1), r));
+ }
+ }
+ var sr = {};
+ function x(t, e) {
+ sr[t] = e;
+ }
+ function H(t, e) {
+ let r = Fn(e);
+ return (
+ Object.entries(sr).forEach(([n, i]) => {
+ Object.defineProperty(t, `$${n}`, {
+ get() {
+ return i(e, r);
+ },
+ enumerable: !1,
+ });
+ }),
+ t
+ );
+ }
+ function Fn(t) {
+ let [e, r] = he(t),
+ n = { interceptor: Tt, ...e };
+ return (et(t, r), n);
+ }
+ function ar(t, e, r, ...n) {
+ try {
+ return r(...n);
+ } catch (i) {
+ nt(i, t, e);
+ }
+ }
+ function nt(...t) {
+ return cr(...t);
+ }
+ var cr = Bn;
+ function lr(t) {
+ cr = t;
+ }
+ function Bn(t, e, r = void 0) {
+ ((t = Object.assign(t ?? { message: "No error message given." }, {
+ el: e,
+ expression: r,
+ })),
+ console.warn(
+ `Alpine Expression Error: ${t.message}
+
+${
+ r
+ ? 'Expression: "' +
+ r +
+ `"
+
+`
+ : ""
+}`,
+ e,
+ ),
+ setTimeout(() => {
+ throw t;
+ }, 0));
+ }
+ var it = !0;
+ function Mt(t) {
+ let e = it;
+ it = !1;
+ let r = t();
+ return ((it = e), r);
+ }
+ function T(t, e, r = {}) {
+ let n;
+ return (_(t, e)((i) => (n = i), r), n);
+ }
+ function _(...t) {
+ return ur(...t);
+ }
+ var ur = () => {};
+ function fr(t) {
+ ur = t;
+ }
+ var dr;
+ function pr(t) {
+ dr = t;
+ }
+ function mr(t, e) {
+ let r = {};
+ H(r, t);
+ let n = [r, ...F(t)],
+ i = typeof e == "function" ? zn(n, e) : Vn(n, e, t);
+ return ar.bind(null, t, e, i);
+ }
+ function zn(t, e) {
+ return (
+ r = () => {},
+ { scope: n = {}, params: i = [], context: o } = {},
+ ) => {
+ if (!it) {
+ ft(r, e, P([n, ...t]), i);
+ return;
+ }
+ let s = e.apply(P([n, ...t]), i);
+ ft(r, s);
+ };
+ }
+ var _e = {};
+ function Hn(t, e) {
+ if (_e[t]) return _e[t];
+ let r = Object.getPrototypeOf(async function () {}).constructor,
+ n =
+ /^[\n\s]*if.*\(.*\)/.test(t.trim()) || /^(let|const)\s/.test(t.trim())
+ ? `(async()=>{ ${t} })()`
+ : t,
+ o = (() => {
+ try {
+ let s = new r(
+ ["__self", "scope"],
+ `with (scope) { __self.result = ${n} }; __self.finished = true; return __self.result;`,
+ );
+ return (
+ Object.defineProperty(s, "name", { value: `[Alpine] ${t}` }),
+ s
+ );
+ } catch (s) {
+ return (nt(s, e, t), Promise.resolve());
+ }
+ })();
+ return ((_e[t] = o), o);
+ }
+ function Vn(t, e, r) {
+ let n = Hn(e, r);
+ return (
+ i = () => {},
+ { scope: o = {}, params: s = [], context: a } = {},
+ ) => {
+ ((n.result = void 0), (n.finished = !1));
+ let c = P([o, ...t]);
+ if (typeof n == "function") {
+ let l = n.call(a, n, c).catch((u) => nt(u, r, e));
+ n.finished
+ ? (ft(i, n.result, c, s, r), (n.result = void 0))
+ : l
+ .then((u) => {
+ ft(i, u, c, s, r);
+ })
+ .catch((u) => nt(u, r, e))
+ .finally(() => (n.result = void 0));
+ }
+ };
+ }
+ function ft(t, e, r, n, i) {
+ if (it && typeof e == "function") {
+ let o = e.apply(r, n);
+ o instanceof Promise
+ ? o.then((s) => ft(t, s, r, n)).catch((s) => nt(s, i, e))
+ : t(o);
+ } else
+ typeof e == "object" && e instanceof Promise ? e.then((o) => t(o)) : t(e);
+ }
+ function hr(...t) {
+ return dr(...t);
+ }
+ function _r(t, e, r = {}) {
+ let n = {};
+ H(n, t);
+ let i = [n, ...F(t)],
+ o = P([r.scope ?? {}, ...i]),
+ s = r.params ?? [];
+ if (e.includes("await")) {
+ let a = Object.getPrototypeOf(async function () {}).constructor,
+ c =
+ /^[\n\s]*if.*\(.*\)/.test(e.trim()) || /^(let|const)\s/.test(e.trim())
+ ? `(async()=>{ ${e} })()`
+ : e;
+ return new a(
+ ["scope"],
+ `with (scope) { let __result = ${c}; return __result }`,
+ ).call(r.context, o);
+ } else {
+ let a =
+ /^[\n\s]*if.*\(.*\)/.test(e.trim()) || /^(let|const)\s/.test(e.trim())
+ ? `(()=>{ ${e} })()`
+ : e,
+ l = new Function(
+ ["scope"],
+ `with (scope) { let __result = ${a}; return __result }`,
+ ).call(r.context, o);
+ return typeof l == "function" && it ? l.apply(o, s) : l;
+ }
+ }
+ var ye = "x-";
+ function O(t = "") {
+ return ye + t;
+ }
+ function gr(t) {
+ ye = t;
+ }
+ var Rt = {};
+ function p(t, e) {
+ return (
+ (Rt[t] = e),
+ {
+ before(r) {
+ if (!Rt[r]) {
+ console.warn(
+ String.raw`Cannot find directive \`${r}\`. \`${t}\` will use the default order of execution`,
+ );
+ return;
+ }
+ let n = G.indexOf(r);
+ G.splice(n >= 0 ? n : G.indexOf("DEFAULT"), 0, t);
+ },
+ }
+ );
+ }
+ function xr(t) {
+ return Object.keys(Rt).includes(t);
+ }
+ function pt(t, e, r) {
+ if (((e = Array.from(e)), t._x_virtualDirectives)) {
+ let o = Object.entries(t._x_virtualDirectives).map(([a, c]) => ({
+ name: a,
+ value: c,
+ })),
+ s = be(o);
+ ((o = o.map((a) =>
+ s.find((c) => c.name === a.name)
+ ? { name: `x-bind:${a.name}`, value: `"${a.value}"` }
+ : a,
+ )),
+ (e = e.concat(o)));
+ }
+ let n = {};
+ return e
+ .map(wr((o, s) => (n[o] = s)))
+ .filter(Sr)
+ .map(qn(n, r))
+ .sort(Kn)
+ .map((o) => Un(t, o));
+ }
+ function be(t) {
+ return Array.from(t)
+ .map(wr())
+ .filter((e) => !Sr(e));
+ }
+ var ge = !1,
+ dt = new Map(),
+ yr = Symbol();
+ function br(t) {
+ ge = !0;
+ let e = Symbol();
+ ((yr = e), dt.set(e, []));
+ let r = () => {
+ for (; dt.get(e).length;) dt.get(e).shift()();
+ dt.delete(e);
+ },
+ n = () => {
+ ((ge = !1), r());
+ };
+ (t(r), n());
+ }
+ function he(t) {
+ let e = [],
+ r = (a) => e.push(a),
+ [n, i] = Ye(t);
+ return (
+ e.push(i),
+ [
+ {
+ Alpine: B,
+ effect: n,
+ cleanup: r,
+ evaluateLater: _.bind(_, t),
+ evaluate: T.bind(T, t),
+ },
+ () => e.forEach((a) => a()),
+ ]
+ );
+ }
+ function Un(t, e) {
+ let r = () => {},
+ n = Rt[e.type] || r,
+ [i, o] = he(t);
+ Ot(t, e.original, o);
+ let s = () => {
+ t._x_ignore ||
+ t._x_ignoreSelf ||
+ (n.inline && n.inline(t, e, i),
+ (n = n.bind(n, t, e, i)),
+ ge ? dt.get(yr).push(n) : n());
+ };
+ return ((s.runCleanups = o), s);
+ }
+ var Nt =
+ (t, e) =>
+ ({ name: r, value: n }) => (
+ r.startsWith(t) && (r = r.replace(t, e)),
+ { name: r, value: n }
+ ),
+ Pt = (t) => t;
+ function wr(t = () => {}) {
+ return ({ name: e, value: r }) => {
+ let { name: n, value: i } = Er.reduce((o, s) => s(o), {
+ name: e,
+ value: r,
+ });
+ return (n !== e && t(n, e), { name: n, value: i });
+ };
+ }
+ var Er = [];
+ function ot(t) {
+ Er.push(t);
+ }
+ function Sr({ name: t }) {
+ return vr().test(t);
+ }
+ var vr = () => new RegExp(`^${ye}([^:^.]+)\\b`);
+ function qn(t, e) {
+ return ({ name: r, value: n }) => {
+ r === n && (n = "");
+ let i = r.match(vr()),
+ o = r.match(/:([a-zA-Z0-9\-_:]+)/),
+ s = r.match(/\.[^.\]]+(?=[^\]]*$)/g) || [],
+ a = e || t[r] || r;
+ return {
+ type: i ? i[1] : null,
+ value: o ? o[1] : null,
+ modifiers: s.map((c) => c.replace(".", "")),
+ expression: n,
+ original: a,
+ };
+ };
+ }
+ var xe = "DEFAULT",
+ G = [
+ "ignore",
+ "ref",
+ "data",
+ "id",
+ "anchor",
+ "bind",
+ "init",
+ "for",
+ "model",
+ "modelable",
+ "transition",
+ "show",
+ "if",
+ xe,
+ "teleport",
+ ];
+ function Kn(t, e) {
+ let r = G.indexOf(t.type) === -1 ? xe : t.type,
+ n = G.indexOf(e.type) === -1 ? xe : e.type;
+ return G.indexOf(r) - G.indexOf(n);
+ }
+ function J(t, e, r = {}, n = {}) {
+ return t.dispatchEvent(
+ new CustomEvent(e, {
+ detail: r,
+ bubbles: !0,
+ composed: !0,
+ cancelable: !0,
+ ...n,
+ }),
+ );
+ }
+ function D(t, e) {
+ if (typeof ShadowRoot == "function" && t instanceof ShadowRoot) {
+ Array.from(t.children).forEach((i) => D(i, e));
+ return;
+ }
+ let r = !1;
+ if ((e(t, () => (r = !0)), r)) return;
+ let n = t.firstElementChild;
+ for (; n;) (D(n, e, !1), (n = n.nextElementSibling));
+ }
+ function E(t, ...e) {
+ console.warn(`Alpine Warning: ${t}`, ...e);
+ }
+ var Ar = !1;
+ function Or() {
+ (Ar &&
+ E(
+ "Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems.",
+ ),
+ (Ar = !0),
+ document.body ||
+ E(
+ "Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine's `