function createFeatureLoader({ document, urls, timeoutMs = 10000 }) {
  const pending = new Map();
  const loaded = new Set();

  function load(name) {
    if (loaded.has(name)) return Promise.resolve();
    if (pending.has(name)) return pending.get(name);
    const url = urls[name];
    if (!url) return Promise.reject(new Error('Unknown feature: ' + name));

    const request = new Promise((resolve, reject) => {
      const script = document.createElement('script');
      let timeout;
      const fail = () => {
        clearTimeout(timeout);
        script.remove();
        pending.delete(name);
        reject(new Error('Could not load ' + name.replace(/-/g, ' ') + '.'));
      };
      script.src = url;
      script.async = true;
      script.onload = () => {
        clearTimeout(timeout);
        pending.delete(name);
        loaded.add(name);
        resolve();
      };
      script.onerror = fail;
      timeout = setTimeout(fail, timeoutMs);
      document.head.appendChild(script);
    });
    pending.set(name, request);
    return request;
  }

  async function run(name, elements, callback) {
    const trigger = elements?.trigger;
    const status = elements?.status;
    const retryLabel = elements?.retryLabel || 'Tap New issue to retry.';
    if (trigger) trigger.disabled = true;
    if (status) status.textContent = 'Loading ' + name.replace(/-/g, ' ') + '…';
    try {
      await load(name);
      callback();
      if (status) status.textContent = '';
      return true;
    } catch (_error) {
      if (status) {
        const label = name === 'issue-capture' ? 'Issue capture' :
          name === 'pull-workflow' ? 'Pull workspace' :
          name === 'security-center' ? 'Security center' : name.replace(/-/g, ' ');
        status.textContent = label + ' could not load. ' + retryLabel;
      }
      return false;
    } finally {
      if (trigger) trigger.disabled = false;
    }
  }

  return { load, run, ready: name => loaded.has(name) };
}

if (typeof module !== 'undefined' && module.exports) module.exports = createFeatureLoader;