// @bun
var{defineProperty:N,getOwnPropertyNames:Y,getOwnPropertyDescriptor:Z}=Object,_=Object.prototype.hasOwnProperty;function $(j){return this[j]}var S=(j)=>{var q=(P??=new WeakMap).get(j),z;if(q)return q;if(q=N({},"__esModule",{value:!0}),j&&typeof j==="object"||typeof j==="function"){for(var w of Y(j))if(!_.call(q,w))N(q,w,{get:$.bind(j,w),enumerable:!(z=Z(j,w))||z.enumerable})}return P.set(j,q),q},P;var f=(j)=>j;function G(j,q){this[j]=f.bind(null,q)}var k=(j,q)=>{for(var z in q)N(j,z,{get:q[z],enumerable:!0,configurable:!0,set:G.bind(q,z)})};var h=(j,q)=>()=>(j&&(q=j(j=0)),q);var v=import.meta.require;var O={};k(O,{processAsyncDirectives:()=>W,isAsyncComponent:()=>R,generateAsyncComponentRuntime:()=>A,generateAsyncComponentCSS:()=>b,defineAsyncComponent:()=>x});function Q(){return`async-${++H}-${Date.now().toString(36)}`}function W(j,q={},z){let w=/@async\s*\(([^)]*)\)\s*([\s\S]*?)@error\s*([\s\S]*?)@endasync/gi,J=/@async\s*\(([^)]*)\)\s*([\s\S]*?)@endasync/gi,B=j;return B=B.replace(w,(D,F,K,L)=>{let M=Q(),X=T(F);return U(M,X,K.trim(),L.trim())}),B=B.replace(J,(D,F,K)=>{let L=Q(),M=T(F);return U(L,M,K.trim(),'<div class="stx-async-error">Failed to load component</div>')}),B}function T(j){let q={component:"",timeout:30000,delay:200,suspensible:!0,retries:0},z=/(\w+)\s*:\s*(?:'([^']*)'|"([^"]*)"|(\d+)|(\w+))/g,w;while((w=z.exec(j))!==null){let J=w[1],B=w[2]||w[3],D=w[4],F=w[5];switch(J){case"component":q.component=B||F||"";break;case"timeout":q.timeout=D?parseInt(D,10):30000;break;case"delay":q.delay=D?parseInt(D,10):200;break;case"suspensible":q.suspensible=F==="true";break;case"retries":q.retries=D?parseInt(D,10):0;break;case"loadingComponent":q.loadingComponent=B||F;break;case"errorComponent":q.errorComponent=B||F;break}}return q}function U(j,q,z,w){let{component:J,timeout:B,delay:D,suspensible:F,retries:K}=q;return`
<div class="stx-async-component" data-async-id="${j}" data-component="${J}" data-status="pending">
  <div class="stx-async-loading" data-async-loading="${j}">
    ${z}
  </div>
  <div class="stx-async-content" data-async-content="${j}" style="display: none;">
  </div>
  <div class="stx-async-error" data-async-error="${j}" style="display: none;">
    ${w}
  </div>
</div>
<script>
(function() {
  const asyncId = '${j}';
  const componentPath = '${J}';
  const timeout = ${B};
  const delay = ${D};
  const suspensible = ${F};
  const maxRetries = ${K};

  const container = document.querySelector('[data-async-id="' + asyncId + '"]');
  const loadingEl = document.querySelector('[data-async-loading="' + asyncId + '"]');
  const contentEl = document.querySelector('[data-async-content="' + asyncId + '"]');
  const errorEl = document.querySelector('[data-async-error="' + asyncId + '"]');

  if (!container || !loadingEl || !contentEl || !errorEl) return;

  let attempts = 0;
  let delayTimer = null;
  let timeoutTimer = null;
  let resolved = false;
  let aborted = false;

  // AbortController + container detachment guards (stacksjs/stx#1720). Without
  // these, an in-flight fetch from a previous page would write innerHTML on
  // a detached container after the user SPA-navigated away, or \u2014 for
  // components removed via :if/parent toggle \u2014 would log a "rejected" state
  // on a node that no longer exists. The router dispatches stx:navigate
  // when a navigation starts; we treat that as a teardown signal. The
  // isConnected check covers non-router unmounts (:if false, parent remove).
  const controller = new AbortController();
  function teardown() {
    if (aborted) return;
    aborted = true;
    controller.abort();
    clearTimeout(delayTimer);
    clearTimeout(timeoutTimer);
    window.removeEventListener('stx:navigate', teardown);
  }
  window.addEventListener('stx:navigate', teardown);

  // Update state
  function setState(status) {
    container.setAttribute('data-status', status);
    loadingEl.style.display = status === 'loading' ? '' : 'none';
    contentEl.style.display = status === 'resolved' ? '' : 'none';
    errorEl.style.display = status === 'rejected' ? '' : 'none';
  }

  // Show loading after delay
  function showLoading() {
    if (!resolved) {
      setState('loading');
    }
  }

  // Load component
  async function loadComponent() {
    attempts++;

    // Start delay timer for loading state
    delayTimer = setTimeout(showLoading, delay);

    // Start timeout timer
    timeoutTimer = setTimeout(function() {
      if (!resolved) {
        handleError(new Error('Component load timeout after ' + timeout + 'ms'));
      }
    }, timeout);

    try {
      // Notify Suspense boundary if suspensible
      if (suspensible && window.STX && window.STX.suspense) {
        window.STX.suspense.register(asyncId);
      }

      // Fetch the component with the AbortController signal so a teardown
      // (SPA navigation, component unmount) cancels the in-flight request.
      const response = await fetch('/_stx/component/' + encodeURIComponent(componentPath), {
        signal: controller.signal
      });

      if (!response.ok) {
        throw new Error('Failed to load component: ' + response.statusText);
      }

      const html = await response.text();

      // Clear timers
      clearTimeout(delayTimer);
      clearTimeout(timeoutTimer);

      // Belt-and-suspenders: if the container was removed between the fetch
      // starting and now (e.g. :if flipped to false, or a non-router parent
      // mutation), bail before writing innerHTML on a detached node.
      if (aborted || !container.isConnected) return;

      resolved = true;
      contentEl.innerHTML = html;
      setState('resolved');

      // Execute any scripts in the loaded component
      contentEl.querySelectorAll('script').forEach(function(script) {
        const newScript = document.createElement('script');
        if (script.src) {
          newScript.src = script.src;
        }
else {
          newScript.textContent = script.textContent;
        }
        script.parentNode.replaceChild(newScript, script);
      });

      // Process signals/bindings on the loaded content ({{ }}, :attr, @event, etc.)
      if (window.stx && window.stx._latestSetup) {
        window.dispatchEvent(new Event('stx:load'));
      }

      // Notify Suspense boundary
      if (suspensible && window.STX && window.STX.suspense) {
        window.STX.suspense.resolve(asyncId);
      }

      // Dispatch loaded event
      container.dispatchEvent(new CustomEvent('stx:async:loaded', {
        detail: { asyncId: asyncId, component: componentPath },
        bubbles: true
      }));

      // Load succeeded \u2014 drop the navigate-teardown listener so it doesn't
      // accumulate per async component across an app's lifetime.
      window.removeEventListener('stx:navigate', teardown);
    }
catch (error) {
      // AbortError fires when teardown() runs during an in-flight fetch.
      // That's not a load failure \u2014 the component is being unmounted, so
      // don't render the rejected state, don't retry, don't dispatch error.
      if (aborted || (error && error.name === 'AbortError')) return;
      handleError(error);
    }
  }

  // Handle errors
  function handleError(error) {
    clearTimeout(delayTimer);
    clearTimeout(timeoutTimer);

    // The container may have been removed (e.g., a non-router :if toggle)
    // between the failure and now. No DOM left to write the error state to.
    if (aborted || !container.isConnected) return;

    if (attempts < maxRetries) {
      // Retry
      console.warn('[stx:async:' + asyncId + '] Retrying... (' + attempts + '/' + maxRetries + ')');
      setTimeout(loadComponent, 1000 * attempts); // Exponential backoff
      return;
    }

    resolved = true;
    setState('rejected');

    // Update error content with error message
    const errorMsgEl = errorEl.querySelector('[data-error-message]');
    if (errorMsgEl) {
      errorMsgEl.textContent = error.message;
    }

    // Notify Suspense boundary
    if (suspensible && window.STX && window.STX.suspense) {
      window.STX.suspense.reject(asyncId, error);
    }

    // Dispatch error event
    container.dispatchEvent(new CustomEvent('stx:async:error', {
      detail: { asyncId: asyncId, component: componentPath, error: error },
      bubbles: true
    }));

    console.error('[stx:async:' + asyncId + ']', error);

    // Final error state \u2014 no more retries. Drop the navigate listener so it
    // doesn't outlive the (failed) component.
    window.removeEventListener('stx:navigate', teardown);
  }

  // Retry function
  function retry() {
    resolved = false;
    attempts = 0;
    setState('pending');
    loadComponent();
  }

  // Expose retry function
  window['$stxAsyncRetry_' + asyncId.replace(/-/g, '_')] = retry;

  // Make $retry available within the error content
  errorEl.querySelectorAll('[onclick*="$retry"]').forEach(function(el) {
    const onclick = el.getAttribute('onclick');
    if (onclick) {
      el.setAttribute('onclick', onclick.replace(/\\$retry\\(\\)/g, "window['$stxAsyncRetry_" + asyncId.replace(/-/g, '_') + "']()"));
    }
  });

  // Expose API
  window.STX = window.STX || {};
  window.STX.asyncComponents = window.STX.asyncComponents || {};
  window.STX.asyncComponents[asyncId] = {
    getStatus: function() { return container.getAttribute('data-status'); },
    retry: retry,
    getAttempts: function() { return attempts; }
  };

  // Start loading
  loadComponent();
})();
</script>
`}function x(j){return{__asyncComponent:!0,...typeof j==="function"?{loader:j}:j}}function R(j){return typeof j==="object"&&j!==null&&"__asyncComponent"in j&&j.__asyncComponent===!0}function b(){return`
/* STX Async Component Styles */
.stx-async-component {
  position: relative;
}

.stx-async-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100px;
}

.stx-async-error {
  padding: 1rem;
  border: 1px solid #ef4444;
  border-radius: 0.5rem;
  background-color: #fef2f2;
  color: #991b1b;
}

.stx-async-error button {
  margin-top: 0.5rem;
  padding: 0.5rem 1rem;
  border: 1px solid #ef4444;
  border-radius: 0.25rem;
  background-color: #ef4444;
  color: white;
  cursor: pointer;
}

.stx-async-error button:hover {
  background-color: #dc2626;
}

/* Loading spinner animation */
@keyframes stx-async-spin {
  to { transform: rotate(360deg); }
}

.stx-async-spinner {
  width: 24px;
  height: 24px;
  border: 2px solid #e5e7eb;
  border-top-color: #3b82f6;
  border-radius: 50%;
  animation: stx-async-spin 0.8s linear infinite;
}

/* Skeleton loading */
.stx-async-skeleton {
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: stx-async-shimmer 1.5s infinite;
  border-radius: 0.25rem;
}

@keyframes stx-async-shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* Dark mode */
@media (prefers-color-scheme: dark) {
  .stx-async-error {
    background-color: #450a0a;
    color: #fecaca;
  }

  .stx-async-skeleton {
    background: linear-gradient(90deg, #374151 25%, #4b5563 50%, #374151 75%);
    background-size: 200% 100%;
  }
}
`}function A(){return`
// STX Async Component Runtime
(function() {
  if (typeof window === 'undefined') return;

  window.STX = window.STX || {};
  window.STX.asyncComponents = window.STX.asyncComponents || {};

  // Load a component dynamically
  window.STX.loadComponent = async function(componentPath, targetSelector, options) {
    options = options || {};
    const target = document.querySelector(targetSelector);
    if (!target) {
      throw new Error('Target element not found: ' + targetSelector);
    }

    const loadingHtml = options.loading || '<div class="stx-async-spinner"></div>';
    target.innerHTML = loadingHtml;

    try {
      const response = await fetch('/_stx/component/' + encodeURIComponent(componentPath));
      if (!response.ok) {
        throw new Error('Failed to load: ' + response.statusText);
      }

      const html = await response.text();
      target.innerHTML = html;

      // Execute scripts
      target.querySelectorAll('script').forEach(function(script) {
        const newScript = document.createElement('script');
        if (script.src) {
          newScript.src = script.src;
        }
else {
          newScript.textContent = script.textContent;
        }
        script.parentNode.replaceChild(newScript, script);
      });

      if (options.onLoad) options.onLoad();
      return true;
    }
catch (error) {
      if (options.error) {
        target.innerHTML = options.error;
      }
      if (options.onError) options.onError(error);
      throw error;
    }
  };

  console.log('[stx] Async component runtime initialized');
})();
`}var H=0;export{W as processAsyncDirectives,R as isAsyncComponent,A as generateAsyncComponentRuntime,b as generateAsyncComponentCSS,x as defineAsyncComponent};
