const SAFETY_MS = 15 * 60 * 1000; const SKIP = new Set(["init", "destroy"]);
const TOASTS = new Set(["toast", "toastOk", "toastError", "toastWarn", "toastResult"]);
const STREAM = new Set(["onStreamEvent", "applyBusEvent", "applySnapshot"]);
const held = new Set(); let fromStream = 0;
let current = null;
const isBusy = (element) => element && element.hasAttribute("data-busy");
function arm(button) {
current = button || null;
if (current) setTimeout(() => current === button && (current = null), 0);
}
function lock(button) {
if (!button || isBusy(button)) return () => {};
const own = !button.disabled && !button.hasAttribute(":disabled") && !button.hasAttribute("x-bind:disabled");
button.setAttribute("data-busy", "");
button.setAttribute("aria-busy", "true");
if (own) button.disabled = true;
let done = false;
const release = () => {
if (done) return;
done = true;
held.delete(release);
clearTimeout(safety);
button.removeAttribute("data-busy");
button.removeAttribute("aria-busy");
if (own) button.disabled = false;
};
const safety = setTimeout(release, SAFETY_MS);
held.add(release);
return release;
}
function wrap(originalFunction) {
return function (...args) {
const button = current;
current = null; let out;
try {
out = originalFunction.apply(this, args);
} catch (error) {
current = button;
throw error;
}
if (button && out && typeof out.then === "function") {
const release = lock(button);
out.then(release, release);
} else {
current = button;
}
return out;
};
}
function wrapToast(originalFunction) {
return function (...args) {
const out = originalFunction.apply(this, args);
if (!fromStream) [...held].forEach((release) => release());
return out;
};
}
function wrapStream(originalFunction) {
return function (...args) {
fromStream++;
try {
return originalFunction.apply(this, args);
} finally {
fromStream--;
}
};
}
function track(obj) {
if (!obj || typeof obj !== "object") return obj;
for (const key of Object.keys(obj)) {
if (SKIP.has(key)) continue;
const descriptor = Object.getOwnPropertyDescriptor(obj, key);
if (descriptor && typeof descriptor.value === "function") {
const wrappedFunction = TOASTS.has(key) ? wrapToast(descriptor.value) : STREAM.has(key) ? wrapStream(descriptor.value) : wrap(descriptor.value);
Object.defineProperty(obj, key, { ...descriptor, value: wrappedFunction });
}
}
return obj;
}
export function registerBusy(Alpine) {
const guard = (event, button) => {
if (isBusy(button)) {
event.preventDefault();
event.stopImmediatePropagation();
return;
}
arm(button);
};
document.addEventListener("click", (event) => guard(event, event.target.closest?.("button")), true);
document.addEventListener("submit", (event) => guard(event, event.submitter), true);
const store = Alpine.store.bind(Alpine);
Alpine.store = (...storeArguments) => (storeArguments.length < 2 ? store(...storeArguments) : store(storeArguments[0], track(storeArguments[1])));
const data = Alpine.data.bind(Alpine);
Alpine.data = (name, factory) =>
data(name, function (...dataArguments) {
return track(factory.apply(this, dataArguments));
});
}