apiV4Js constant

String const apiV4Js

API v4 console JavaScript code

Implementation

static const String apiV4Js = r'''(function () {
  const REGISTRAR_ENVS = {
      dev: 'https://my.atsign.wtf',
      prod: 'https://my.atsign.com'
  };
  const STATUS_ENVS = {
      dev: 'https://directory.atsign.wtf',
      prod: 'https://wavi.ng'
  };
  const REQUEST_TIMEOUT_MS = 15000;
  const activationPollers = new Map();

  const PRIMARY_ENDPOINTS = [
      {
          id: 'lookup-atsign-v4',
          title: 'Lookup an Atsign',
          method: 'POST',
          baseType: 'registrar',
          path: '/api/app/v4/register-atsign/',
          authRequired: true,
          params: [
              {
                  name: 'atSign',
                  label: 'Atsign',
                  required: false,
                  type: 'text',
                  placeholder: 'meow01',
                  help: 'Optional. Do not include the prefix/postfix. The prefix/postfix is already implied by the Authorization API Key. So for example, entering "test01" as your Atsign may provision "test01_np". Do not include the @ symbol.'
              },
              { name: 'operation', required: true, type: 'select', options: ['lookup'], defaultValue: 'lookup', disabled: true, help: 'Required' },
              { name: 'startAtServer', required: false, type: 'select', options: ['true', 'false'], defaultValue: 'true', help: 'Optional' }
          ]
      },
      {
          id: 'register-atsign-v4',
          title: 'Register an Atsign',
          method: 'POST',
          baseType: 'registrar',
          path: '/api/app/v4/register-atsign/',
          authRequired: true,
          params: [
              {
                  name: 'atSign',
                  label: 'Atsign',
                  required: false,
                  type: 'text',
                  placeholder: 'meow01',
                  help: 'Optional. Do not include the prefix/postfix. The prefix/postfix is already implied by the Authorization API Key. So for example, entering "test01" as your Atsign may provision "test01_np". Do not include the @ symbol.'
              },
              { name: 'operation', required: true, type: 'select', options: ['register'], defaultValue: 'register', disabled: true, help: 'Required' },
              { name: 'startAtServer', required: false, type: 'select', options: ['true', 'false'], defaultValue: 'true', help: 'Optional' }
          ]
      },
      {
          id: 'register-atsign-v4-batch',
          title: 'Register an Atsign (v4) (BATCH)',
          method: 'POST',
          baseType: 'registrar',
          path: '/api/app/v4/register-atsign/',
          authRequired: true,
          type: 'batch-register',
          params: [
              {
                  name: 'atSigns',
                  label: 'Atsigns',
                  required: true,
                  type: 'textarea',
                  placeholder: 'meow01\nmeow02\nmeow03',
                  help: 'One Atsign per line. Do not include the prefix/postfix. The prefix/postfix is already implied by the Authorization API Key. So for example, entering "test01", "test02", and "test03" as your Atsigns will provision "test01_np", "test02_np", and "test03_np". Do not include the @ symbol.'
              },
              { name: 'startAtServer', required: false, type: 'select', options: ['true', 'false'], defaultValue: 'true', help: 'Optional' }
          ]
      },
      {
          id: 'reset-atsign',
          title: 'Reset an Atsign',
          method: 'POST',
          baseType: 'registrar',
          path: '/api/app/v4/reset-atsign/',
          authRequired: true,
          params: [
              { name: 'atSign', label: 'Atsign', required: true, type: 'text', placeholder: 'meow01_jttest', help: 'Required. Include the full prefix/postfix. For example, if your postfix is _jttest, enter meow01_jttest. This intentionally differs from the other Atsign endpoints.' }
          ]
      },
      {
          id: 'delete-atsigns',
          title: 'Delete Atsigns',
          method: 'POST',
          baseType: 'registrar',
          path: '/api/app/v4/manage-atsigns',
          authRequired: true,
          type: 'delete-atsigns',
          params: [
              {
                  name: 'atSigns',
                  label: 'Atsigns',
                  required: true,
                  type: 'textarea',
                  placeholder: 'meow01_jttest\nmeow02_jttest',
                  help: 'Required. One Atsign per line. Include the full prefix/postfix, the same way the reset endpoint does. The Authorization API Key can only delete Atsigns it created, so anything else comes back as skipped. Do not include the @ symbol. A name that has already been deleted once is refused on later deletes, so avoid reusing one.'
              }
          ]
      }
  ];

  const OTHER_ENDPOINTS = [
      {
          id: 'get-free-atsign-get',
          title: 'Get Free Atsign',
          method: 'GET',
          baseType: 'registrar',
          path: '/api/app/v4/get-free-atsign/',
          authRequired: true,
          params: []
      },
      {
          id: 'get-free-atsign-post',
          title: 'Get Free Atsign by Category',
          method: 'POST',
          baseType: 'registrar',
          path: '/api/app/v4/get-free-atsign/',
          authRequired: true,
          params: [
              { name: 'category', required: false, type: 'csv-array', placeholder: 'animals,movies', help: 'Comma-separated list' }
          ]
      },
      {
          id: 'register-person',
          title: 'Register Person',
          method: 'POST',
          baseType: 'registrar',
          path: '/api/app/v4/register-person/',
          authRequired: true,
          params: [
              { name: 'atsign', label: 'Atsign', required: true, type: 'text', placeholder: 'wisefrog', help: 'Generated free Atsign' },
              { name: 'email', required: true, type: 'text', placeholder: 'user@example.com', help: 'Required' },
              { name: 'oldEmail', required: false, type: 'text', placeholder: 'old@example.com', help: 'Optional' }
          ]
      },
      {
          id: 'validate-person',
          title: 'Validate Person',
          method: 'POST',
          baseType: 'registrar',
          path: '/api/app/v4/validate-person/',
          authRequired: true,
          params: [
              { name: 'atsign', label: 'Atsign', required: true, type: 'text', placeholder: 'wisefrog', help: 'Required' },
              { name: 'email', required: true, type: 'text', placeholder: 'user@example.com', help: 'Required' },
              { name: 'otp', required: true, type: 'text', placeholder: 'AB12', help: '4-character code' },
              { name: 'confirmation', required: false, type: 'boolean', placeholder: 'false', help: 'Optional, defaults to false' }
          ]
      },
      {
          id: 'authenticate-person-atsign',
          title: 'Authenticate Person with Atsign',
          method: 'POST',
          baseType: 'registrar',
          path: '/api/app/v4/authenticate/atsign',
          authRequired: true,
          params: [
              { name: 'atsign', label: 'Atsign', required: true, type: 'text', placeholder: '@alice', help: 'Registered Atsign' }
          ]
      },
      {
          id: 'validate-person-atsign-otp',
          title: 'Validate Person with Atsign and OTP',
          method: 'POST',
          baseType: 'registrar',
          path: '/api/app/v4/authenticate/atsign/activate',
          authRequired: true,
          params: [
              { name: 'atsign', label: 'Atsign', required: true, type: 'text', placeholder: '@alice', help: 'Registered Atsign' },
              { name: 'otp', required: true, type: 'text', placeholder: 'AB12', help: '4-character code' }
          ]
      },
      {
          id: 'get-atsign-v3',
          title: 'Get Atsign and ActivationKey (v3)',
          method: 'POST',
          baseType: 'registrar',
          path: '/api/app/v3/get-atsign/',
          authRequired: true,
          params: [
              { name: 'atSign', label: 'Atsign', required: false, type: 'text', placeholder: 'embergarden', help: 'Optional' },
              { name: 'ActivationKey', required: false, type: 'text', placeholder: '4V3GbA4u', help: 'Optional' }
          ]
      },
      {
          id: 'get-atsign-v4',
          title: 'Get Atsign and ActivationKey (v4)',
          method: 'POST',
          baseType: 'registrar',
          path: '/api/app/v4/get-atsign/',
          authRequired: true,
          params: [
              { name: 'atSign', label: 'Atsign', required: false, type: 'text', placeholder: 'emberly', help: 'Optional' },
              { name: 'ActivationKey', required: false, type: 'text', placeholder: '4V3GbA4u', help: 'Optional' }
          ]
      },
      {
          id: 'activate-atsign',
          title: 'Activate an Atsign',
          method: 'POST',
          baseType: 'registrar',
          path: '/api/app/v4/activate-atsign/',
          authRequired: false,
          params: [
              { name: 'atSign', label: 'Atsign', required: true, type: 'text', placeholder: '@alice', help: 'Required' },
              { name: 'activationKey', required: true, type: 'text', placeholder: '4V3GbA4u', help: 'Required' }
          ]
      },
      {
          id: 'register-atsign-v3',
          title: 'Register an Atsign (v3)',
          method: 'POST',
          baseType: 'registrar',
          path: '/api/app/v3/register-atsign/',
          authRequired: true,
          params: [
              { name: 'atSign', label: 'Atsign', required: false, type: 'text', placeholder: 'mydog_01', help: 'Optional' },
              { name: 'operation', required: true, type: 'select', options: ['lookup', 'register'], help: 'Required' }
          ]
      }
  ];

  const ALL_ENDPOINTS = [...PRIMARY_ENDPOINTS, ...OTHER_ENDPOINTS];

  function envOptions(baseType) {
      const source = baseType === 'status' ? STATUS_ENVS : REGISTRAR_ENVS;
      return Object.keys(source)
          .map((key) => `<option value="${key}" ${key === 'prod' ? 'selected' : ''}>${key} - ${source[key]}</option>`)
          .join('');
  }

  function resolveBase(baseType, env) {
      return baseType === 'status' ? STATUS_ENVS[env] : REGISTRAR_ENVS[env];
  }

  function paramInput(param) {
      if (param.type === 'select') {
          return `
              <select data-param-name="${param.name}" ${param.disabled ? 'disabled' : ''}>
                  <option value=""></option>
                  ${param.options.map((opt) => `<option value="${opt}" ${param.defaultValue === opt ? 'selected' : ''}>${opt}</option>`).join('')}
              </select>
          `;
      }

      if (param.type === 'boolean') {
          return `
              <select data-param-name="${param.name}">
                  <option value=""></option>
                  <option value="true">true</option>
                  <option value="false">false</option>
              </select>
          `;
      }

      if (param.type === 'textarea') {
          return `<textarea data-param-name="${param.name}" rows="8" placeholder="${param.placeholder || ''}"></textarea>`;
      }

      return `<input type="text" data-param-name="${param.name}" placeholder="${param.placeholder || ''}" />`;
  }

  function requiredSummary(endpoint) {
      const required = endpoint.params.filter((p) => p.required).map((p) => p.name);
      return required.length ? `Required parameters: ${required.join(', ')}` : 'Required parameters: none';
  }

  function normalizeParamHelpText(helpText) {
      if (!helpText) {
          return '';
      }

      return String(helpText).replace(/^(required|optional)\.?\s*/i, '').trim();
  }

  function endpointMarkup(endpoint) {
      return `
          <div class="api-v4-row" data-endpoint-id="${endpoint.id}">
              <button type="button" class="api-v4-row-head" data-role="toggle" aria-expanded="false">
                  <div>
                      <div class="api-v4-row-title">${endpoint.title}</div>
                      <div class="api-v4-param-summary">${requiredSummary(endpoint)}</div>
                  </div>
                  <div class="api-v4-row-route">
                      <span class="api-v4-method api-v4-method-${endpoint.method.toLowerCase()}">${endpoint.method}</span>
                      <code>${endpoint.path}</code>
                      <span class="api-v4-toggle-indicator" aria-hidden="true"></span>
                  </div>
              </button>

              <div class="api-v4-row-body" data-role="body" hidden>
                  <div class="api-v4-param-grid">
                      <label class="api-v4-field">
                          <span>Environment</span>
                          <select data-role="env">${envOptions(endpoint.baseType)}</select>
                      </label>
                      <label class="api-v4-field">
                          <span>Authorization API Key ${endpoint.authRequired ? '<small>(required)</small>' : '<small>(not required for this call)</small>'}</span>
                          <input type="password" data-role="api-key" placeholder="Your registrar API key" ${endpoint.authRequired ? '' : ''} />
                      </label>
                      ${endpoint.params.map((param) => `
                          <label class="api-v4-field">
                              <span>${param.label || param.name} <small>${param.required ? 'required' : 'optional'}${normalizeParamHelpText(param.help) ? ` ยท ${normalizeParamHelpText(param.help)}` : ''}</small></span>
                              ${paramInput(param)}
                          </label>
                      `).join('')}
                  </div>

                  <div class="api-v4-inline-warning" data-role="warning" hidden></div>

                  <div class="api-v4-actions">
                      <button type="button" data-role="send">Send</button>
                      <button type="button" class="api-v4-secondary" data-role="clear">Clear Output</button>
                  </div>

                  <div class="api-v4-result" data-role="result" hidden></div>
                  <div class="api-v4-batch-results" data-role="batch-results" hidden></div>
                  <div class="api-v4-status" data-role="status">Ready</div>
                  <pre class="api-v4-output" data-role="output"></pre>
              </div>
          </div>
      `;
  }

  function otherSectionMarkup() {
      if (OTHER_ENDPOINTS.length === 0) {
          return '';
      }

      return `
          <details class="api-v4-section">
              <summary>Other</summary>
              <div class="api-v4-section-body">
                  ${OTHER_ENDPOINTS.map(endpointMarkup).join('')}
              </div>
          </details>
      `;
  }

  function parseJsonObject(value, fieldName) {
      const trimmed = value.trim();
      if (!trimmed) return {};
      try {
          const parsed = JSON.parse(trimmed);
          if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
              return parsed;
          }
          throw new Error(`${fieldName} must be a JSON object`);
      } catch (err) {
          throw new Error(`Invalid ${fieldName}: ${err.message}`);
      }
  }

  function collectPayload(row, endpoint) {
      const payload = {};
      const warning = row.querySelector('[data-role="warning"]');
      if (warning) {
          warning.hidden = true;
          warning.textContent = '';
      }

      for (const param of endpoint.params) {
          const el = row.querySelector(`[data-param-name="${param.name}"]`);
          let raw = (el ? el.value : '').trim();

          if (!raw) {
              if (param.required) {
                  throw new Error(`Missing required parameter: ${param.name}`);
              }
              continue;
          }

          if ((endpoint.id === 'register-atsign-v4-batch' || endpoint.id === 'delete-atsigns') && param.name === 'atSigns') {
              const normalizedValues = raw
                  .split('\n')
                  .map((value) => normalizeAtSignForRequest(value.trim()))
                  .filter(Boolean);
              payload[param.name] = normalizedValues;
              if (el) {
                  el.value = normalizedValues.join('\n');
              }
              if (warning && normalizedValues.some((value, index) => value !== raw.split('\n').map((entry) => entry.trim()).filter(Boolean)[index])) {
                  warning.textContent = 'Warning: leading @ was removed from one or more Atsigns before sending.';
                  warning.hidden = false;
              }
              continue;
          }

          if (
              (
                  endpoint.id === 'register-atsign-v4' ||
                  endpoint.id === 'reset-atsign'
              ) &&
              param.name === 'atSign' &&
              raw.startsWith('@')
          ) {
              raw = raw.substring(1);
              if (el) {
                  el.value = raw;
              }
              if (warning) {
                  warning.textContent = 'Warning: leading @ was removed from Atsign before sending.';
                  warning.hidden = false;
              }
          }

          if (param.type === 'csv-array') {
              payload[param.name] = raw.split(',').map((v) => v.trim()).filter(Boolean);
          } else if (param.type === 'boolean') {
              payload[param.name] = raw === 'true';
          } else {
              payload[param.name] = raw;
          }
      }

      return payload;
  }

  function buildUrl(endpoint, env, payload) {
      let path = endpoint.path;
      if (path.includes('{atSign}')) {
          path = path.replace('{atSign}', encodeURIComponent(payload.atSign || payload.atsign || ''));
          delete payload.atSign;
      }
      return `${resolveBase(endpoint.baseType, env)}${path}`;
  }

  function renderPrimarySummary(row, endpoint, parsedBody) {
      const result = row.querySelector('[data-role="result"]');
      if (!result) return;

      if (
          !PRIMARY_ENDPOINTS.some((item) => item.id === endpoint.id) ||
          endpoint.id === 'register-atsign-v4-batch' ||
          !parsedBody ||
          typeof parsedBody !== 'object'
      ) {
          result.hidden = true;
          result.innerHTML = '';
          return;
      }

      const apiStatus = parsedBody.status === 'success' ? 'success' : 'error';
      const badgeLabel = apiStatus === 'success' ? 'Success' : 'Error';
      let summaryText = parsedBody.message || 'Request completed.';
      if (endpoint.id === 'register-atsign-v4') {
          if (apiStatus === 'success' && parsedBody.cramkey) {
              summaryText = 'Atsign registered successfully.';
          } else if (parsedBody.message) {
              summaryText = parsedBody.message;
          }
      }

      if (endpoint.id === 'lookup-atsign-v4' && parsedBody.message) {
          summaryText = parsedBody.message;
      }

      if (endpoint.id === 'reset-atsign' && parsedBody.message) {
          summaryText = parsedBody.message;
      }

      const cramkeyBlock = parsedBody.cramkey
          ? `<div class="api-v4-cramkey-block"><div class="api-v4-cramkey-label">cramkey</div><code>${escapeHtml(parsedBody.cramkey)}</code></div>`
          : '';

      result.innerHTML = `
          <div class="api-v4-result-card api-v4-result-${apiStatus}">
              <div class="api-v4-result-head">
                  <span class="api-v4-result-badge api-v4-result-badge-${apiStatus}">${badgeLabel}</span>
                  <span class="api-v4-result-text">${escapeHtml(summaryText)}</span>
              </div>
              ${cramkeyBlock}
          </div>
      `;
      result.hidden = false;
  }

  function escapeHtml(value) {
      return String(value)
          .replaceAll('&', '&amp;')
          .replaceAll('<', '&lt;')
          .replaceAll('>', '&gt;')
          .replaceAll('"', '&quot;')
          .replaceAll("'", '&#39;');
  }

  function extractAtSignFromCramkey(cramkey) {
      if (!cramkey || typeof cramkey !== 'string') {
          return '';
      }

      const separatorIndex = cramkey.indexOf(':');
      return separatorIndex >= 0 ? cramkey.substring(0, separatorIndex) : cramkey;
  }

  function extractCramKeyValue(cramkey) {
      if (!cramkey || typeof cramkey !== 'string') {
          return '';
      }

      const separatorIndex = cramkey.indexOf(':');
      return separatorIndex >= 0 ? cramkey.substring(separatorIndex + 1) : cramkey;
  }

  function parseRequestBody(data) {
      const request = data && data.request ? data.request : {};
      const body = request.body;
      if (!body || typeof body !== 'string') {
          return {};
      }

      try {
          const parsed = JSON.parse(body);
          return parsed && typeof parsed === 'object' ? parsed : {};
      } catch (_) {
          return {};
      }
  }

  function transactionTypeLabel(type) {
      if (type === 'register-atsign') {
          return 'Register an Atsign';
      }
      if (type === 'reset-atsign') {
          return 'Reset an Atsign';
      }
      if (type === 'delete-atsign') {
          return 'Delete Atsigns';
      }
      return type || 'Transaction';
  }

  function summarizeTransaction(item) {
      const data = item.data || {};
      const request = data.request || {};
      const response = data.response || {};
      const responseBody = response.body || {};
      const requestBody = parseRequestBody(data);
      const cramkey = responseBody.cramkey || '';
      const cramAtSign = extractAtSignFromCramkey(cramkey);
      const requestedAtSign = requestBody.atSign ? `@${String(requestBody.atSign).replace(/^@/, '')}` : '';
      const requestedAtSigns = Array.isArray(requestBody.atSigns)
          ? requestBody.atSigns.map((value) => `@${String(value).replace(/^@/, '')}`).join(', ')
          : '';
      const label = cramAtSign || requestedAtSign || requestedAtSigns || item.filename || 'Transaction';
      const apiStatus = responseBody.status || (response.ok ? 'success' : 'error');
      const message = responseBody.message || '';
      const createdAt = data.createdAtUtc || '';

      return {
          data,
          request,
          response,
          responseBody,
          cramkey,
          label,
          apiStatus,
          message,
          createdAt,
          typeLabel: transactionTypeLabel(data.type)
      };
  }

  function renderActivationCramKeys(items) {
      const list = document.getElementById('activation-list');
      if (!list) return;

      if (!items || items.length === 0) {
          list.innerHTML = '<div class="api-v4-transaction-card"><div class="api-v4-transaction-meta">No pending CRAM key files found.</div></div>';
          return;
      }

      list.innerHTML = items.map((item) => {
          const data = item.data || {};
          const atSign = data.atSign || item.filename || 'Unknown';
          const cramKey = data.cramKey || '';
          const createdAt = data.createdAtUtc || '';
          const sourceTransactionPath = data.sourceTransactionPath || '';

          return `
              <details class="api-v4-transaction-card">
                  <summary class="api-v4-transaction-summary">
                      <div class="api-v4-transaction-title-wrap">
                          <div class="api-v4-transaction-title">${escapeHtml(atSign)}</div>
                      </div>
                      <div class="api-v4-transaction-summary-right">
                          <button type="button" class="api-v4-secondary" data-activate-cramkey="${escapeHtml(atSign)}">Activate</button>
                          <span class="api-v4-result-badge api-v4-result-badge-success" data-activation-badge="${escapeHtml(atSign)}">pending</span>
                          <span class="api-v4-transaction-created">${escapeHtml(createdAt)}</span>
                      </div>
                  </summary>
                  <div class="api-v4-transaction-body">
                      <div class="api-v4-transaction-meta"><strong>Path:</strong> ${escapeHtml(item.path || '')}</div>
                      ${sourceTransactionPath ? `<div class="api-v4-transaction-meta"><strong>Source transaction:</strong> ${escapeHtml(sourceTransactionPath)}</div>` : ''}
                      <div class="api-v4-transaction-cramkey">
                          <code>${escapeHtml(cramKey)}</code>
                          <button type="button" class="api-v4-secondary" data-copy-activation-cramkey="${escapeHtml(cramKey)}">Copy CRAM Key</button>
                      </div>
                      <div class="api-v4-status" data-activation-item-status="${escapeHtml(atSign)}">Ready</div>
                      <div class="api-v4-transaction-meta" data-activation-command="${escapeHtml(atSign)}" hidden></div>
                      <pre class="api-v4-activation-log" data-activation-log="${escapeHtml(atSign)}" hidden></pre>
                  </div>
              </details>
          `;
      }).join('');

      list.querySelectorAll('[data-copy-activation-cramkey]').forEach((button) => {
          button.addEventListener('click', async function (event) {
              event.preventDefault();
              event.stopPropagation();

              const value = button.getAttribute('data-copy-activation-cramkey') || '';
              await navigator.clipboard.writeText(value);
              const originalText = button.textContent;
              button.textContent = 'Copied';
              setTimeout(() => {
                  button.textContent = originalText;
              }, 1500);
          });
      });

      list.querySelectorAll('[data-activate-cramkey]').forEach((button) => {
          button.addEventListener('click', async function (event) {
              event.preventDefault();
              event.stopPropagation();
              const atSign = button.getAttribute('data-activate-cramkey') || '';
              await activateCramKey(atSign, button);
          });
      });
  }

  function renderOnboardedCramKeys(items) {
      const list = document.getElementById('activation-onboarded-list');
      if (!list) return;

      if (!items || items.length === 0) {
          list.innerHTML = '<div class="api-v4-transaction-card"><div class="api-v4-transaction-meta">No onboarded CRAM key files found.</div></div>';
          return;
      }

      list.innerHTML = items.map((item) => {
          const data = item.data || {};
          const atSign = data.atSign || item.filename || 'Unknown';
          const onboardedAt = data.onboardedAtUtc || '';
          const sourceTransactionPath = data.sourceTransactionPath || '';

          return `
              <details class="api-v4-transaction-card">
                  <summary class="api-v4-transaction-summary">
                      <div class="api-v4-transaction-title-wrap">
                          <div class="api-v4-transaction-title">${escapeHtml(atSign)}</div>
                          <div class="api-v4-transaction-subtitle">Onboarded ${escapeHtml(onboardedAt || 'Unknown time')}</div>
                      </div>
                      <div class="api-v4-transaction-summary-right">
                          <button type="button" class="api-v4-secondary" data-open-onboarded-apkam="${escapeHtml(atSign)}">Go to APKAM Management</button>
                      </div>
                  </summary>
                  <div class="api-v4-transaction-body">
                      <div class="api-v4-transaction-meta"><strong>Path:</strong> ${escapeHtml(item.path || '')}</div>
                      ${sourceTransactionPath ? `<div class="api-v4-transaction-meta"><strong>Source transaction:</strong> ${escapeHtml(sourceTransactionPath)}</div>` : ''}
                      ${onboardedAt ? `<div class="api-v4-transaction-meta"><strong>Onboarded at:</strong> ${escapeHtml(onboardedAt)}</div>` : ''}
                  </div>
              </details>
          `;
      }).join('');

      list.querySelectorAll('[data-open-onboarded-apkam]').forEach((button) => {
          button.addEventListener('click', async function (event) {
              event.preventDefault();
              event.stopPropagation();
              const atSign = button.getAttribute('data-open-onboarded-apkam') || '';
              try {
                  await openAtSignInApkamManagement(atSign);
              } catch (err) {
                  const status = document.getElementById('activation-status');
                  if (status) status.textContent = `Failed to open APKAM Management: ${err.message}`;
              }
          });
      });
  }

  function renderTransactions(transactions) {
      const list = document.getElementById('api-v4-transactions-list');
      if (!list) return;

      if (!transactions || transactions.length === 0) {
          list.innerHTML = '<div class="api-v4-transaction-card"><div class="api-v4-transaction-meta">No transaction files found.</div></div>';
          return;
      }

      list.innerHTML = transactions.map((item) => {
          const summary = summarizeTransaction(item);
          const badgeClass = summary.apiStatus === 'success' ? 'success' : 'error';
          const title = summary.label;
          const subtitle = summary.message || summary.typeLabel;
          return `
              <details class="api-v4-transaction-card">
                  <summary class="api-v4-transaction-summary">
                      <div class="api-v4-transaction-title-wrap">
                          <div class="api-v4-transaction-title">${escapeHtml(title)}</div>
                          <div class="api-v4-transaction-subtitle">${escapeHtml(subtitle)}</div>
                      </div>
                      <div class="api-v4-transaction-summary-right">
                          <span class="api-v4-result-badge api-v4-result-badge-${badgeClass}">${escapeHtml(summary.apiStatus)}</span>
                          <span class="api-v4-transaction-created">${escapeHtml(summary.createdAt)}</span>
                      </div>
                  </summary>
                  <div class="api-v4-transaction-body">
                      <div class="api-v4-transaction-meta"><strong>Type:</strong> ${escapeHtml(summary.typeLabel)}</div>
                      <div class="api-v4-transaction-meta"><strong>URL:</strong> ${escapeHtml(summary.request.url || '')}</div>
                      <div class="api-v4-transaction-meta"><strong>Path:</strong> ${escapeHtml(item.path || '')}</div>
                      ${summary.message ? `<div class="api-v4-transaction-meta"><strong>Message:</strong> ${escapeHtml(summary.message)}</div>` : ''}
                      ${summary.cramkey ? `
                          <div class="api-v4-transaction-cramkey">
                              <code>${escapeHtml(summary.cramkey)}</code>
                              <button type="button" class="api-v4-secondary" data-copy-transaction-cramkey="${escapeHtml(extractCramKeyValue(summary.cramkey))}">Copy CRAM Key</button>
                          </div>
                      ` : ''}
                      <details class="api-v4-transaction-raw">
                          <summary>Raw JSON</summary>
                          <pre class="api-v4-transaction-json">${escapeHtml(JSON.stringify(summary.data, null, 2))}</pre>
                      </details>
                  </div>
              </details>
          `;
      }).join('');

      list.querySelectorAll('[data-copy-transaction-cramkey]').forEach((button) => {
          button.addEventListener('click', async function (event) {
              event.preventDefault();
              event.stopPropagation();

              const value = button.getAttribute('data-copy-transaction-cramkey') || '';
              await navigator.clipboard.writeText(value);
              const originalText = button.textContent;
              button.textContent = 'Copied';
              setTimeout(() => {
                  button.textContent = originalText;
              }, 1500);
          });
      });
  }

  async function loadApiV4Transactions() {
      const status = document.getElementById('api-v4-transactions-status');
      const list = document.getElementById('api-v4-transactions-list');
      if (!status || !list) return;

      status.textContent = 'Loading transactions...';
      list.innerHTML = '';

      try {
          const response = await fetch('/api/transactions');
          const data = await response.json();
          if (!response.ok || data.error) {
              throw new Error(data.error || 'Failed to load transactions');
          }
          renderTransactions(data.transactions || []);
          status.textContent = `Loaded ${data.transactions ? data.transactions.length : 0} transaction files`;
      } catch (err) {
          status.textContent = `Load failed: ${err.message}`;
          list.innerHTML = '<div class="api-v4-transaction-card"><div class="api-v4-transaction-meta">Failed to load transactions.</div></div>';
      }
  }

  async function openTransactionsFolder() {
      const status = document.getElementById('api-v4-transactions-status') || document.getElementById('api-v4-save-status');
      if (status) status.textContent = 'Opening transactions folder...';

      try {
          const response = await fetch('/api/transactions/open-folder', {
              method: 'POST'
          });
          const data = await response.json();
          if (!response.ok || data.error) {
              throw new Error(data.error || 'Failed to open transactions folder');
          }
          if (status) status.textContent = `Opened transactions folder: ${data.path}`;
      } catch (err) {
          if (status) status.textContent = `Open failed: ${err.message}`;
      }
  }

  async function loadActivationCramKeys() {
      const status = document.getElementById('activation-status');
      const list = document.getElementById('activation-list');
      const onboardedList = document.getElementById('activation-onboarded-list');
      if (!status || !list || !onboardedList) return;

      status.textContent = 'Loading CRAM key files...';
      list.innerHTML = '';
      onboardedList.innerHTML = '';

      try {
          const [pendingResponse, archivedResponse] = await Promise.all([
              fetch('/api/cramkeys'),
              fetch('/api/cramkeys/archived')
          ]);
          const pendingData = await pendingResponse.json();
          const archivedData = await archivedResponse.json();
          if (!pendingResponse.ok || pendingData.error) {
              throw new Error(pendingData.error || 'Failed to load CRAM key files');
          }
          if (!archivedResponse.ok || archivedData.error) {
              throw new Error(archivedData.error || 'Failed to load onboarded CRAM key files');
          }

          renderActivationCramKeys(pendingData.cramKeys || []);
          renderOnboardedCramKeys(archivedData.cramKeys || []);
          status.textContent = `Loaded ${pendingData.cramKeys ? pendingData.cramKeys.length : 0} pending and ${archivedData.cramKeys ? archivedData.cramKeys.length : 0} onboarded CRAM key files`;
      } catch (err) {
          status.textContent = `Load failed: ${err.message}`;
          list.innerHTML = '<div class="api-v4-transaction-card"><div class="api-v4-transaction-meta">Failed to load CRAM key files.</div></div>';
          onboardedList.innerHTML = '<div class="api-v4-transaction-card"><div class="api-v4-transaction-meta">Failed to load onboarded CRAM key files.</div></div>';
      }
  }

  async function openCramKeysFolder() {
      const status = document.getElementById('activation-status');
      if (status) status.textContent = 'Opening cramkeys folder...';

      try {
          const response = await fetch('/api/cramkeys/open-folder', {
              method: 'POST'
          });
          const data = await response.json();
          if (!response.ok || data.error) {
              throw new Error(data.error || 'Failed to open cramkeys folder');
          }
          if (status) status.textContent = `Opened cramkeys folder: ${data.path}`;
      } catch (err) {
          if (status) status.textContent = `Open failed: ${err.message}`;
      }
  }

  async function activateCramKey(atSign, button) {
      const itemStatus = document.querySelector(`[data-activation-item-status="${CSS.escape(atSign)}"]`);
      const badgeEl = document.querySelector(`[data-activation-badge="${CSS.escape(atSign)}"]`);
      const commandEl = document.querySelector(`[data-activation-command="${CSS.escape(atSign)}"]`);
      const logEl = document.querySelector(`[data-activation-log="${CSS.escape(atSign)}"]`);
      if (button) {
          button.disabled = true;
      }
      if (itemStatus) {
          itemStatus.textContent = 'Activating...';
      }
      if (badgeEl) {
          badgeEl.textContent = 'running';
          badgeEl.classList.remove('api-v4-result-badge-neutral');
          badgeEl.classList.add('api-v4-result-badge-success');
      }
      if (commandEl) {
          commandEl.hidden = true;
          commandEl.textContent = '';
      }
      if (logEl) {
          logEl.hidden = true;
          logEl.textContent = '';
      }

      try {
          const response = await fetch('/api/cramkeys/activate/start', {
              method: 'POST',
              headers: {
                  'Content-Type': 'application/json'
              },
              body: JSON.stringify({ atSign })
          });
          const data = await response.json();
          if (!response.ok || data.error) {
              throw new Error(data.error || 'Activation failed');
          }

          if (commandEl) {
              commandEl.hidden = false;
              commandEl.innerHTML = `<strong>Command:</strong> <code>${escapeHtml(data.command || '')}</code>`;
          }
          if (logEl) {
              logEl.hidden = false;
              logEl.textContent = 'Activation started...\n';
          }
          await pollActivationJob(data.jobId, data.atSign, button);
      } catch (err) {
          if (itemStatus) {
              itemStatus.textContent = `Activation failed: ${err.message}`;
          }
          if (badgeEl) {
              badgeEl.textContent = 'error';
              badgeEl.classList.remove('api-v4-result-badge-success', 'api-v4-result-badge-neutral');
              badgeEl.classList.add('api-v4-result-badge-error');
          }
          if (button) {
              button.disabled = false;
          }
      } finally {
      }
  }

  async function pollActivationJob(jobId, atSign, button) {
      const itemStatus = document.querySelector(`[data-activation-item-status="${CSS.escape(atSign)}"]`);
      const badgeEl = document.querySelector(`[data-activation-badge="${CSS.escape(atSign)}"]`);
      const logEl = document.querySelector(`[data-activation-log="${CSS.escape(atSign)}"]`);

      const renderJob = function (job) {
          if (logEl) {
              const stdout = Array.isArray(job.stdout) ? job.stdout : [];
              const stderr = Array.isArray(job.stderr) ? job.stderr : [];
              const lines = [];
              if (stdout.length) {
                  lines.push(...stdout.map((line) => `[stdout] ${line}`));
              }
              if (stderr.length) {
                  lines.push(...stderr.map((line) => `[stderr] ${line}`));
              }
              logEl.hidden = false;
              logEl.textContent = lines.length ? lines.join('\n') : 'No process output yet...';
          }

          if (job.status === 'running') {
              if (itemStatus) {
                  itemStatus.textContent = 'Running...';
              }
              return false;
          }

          if (job.status === 'success') {
              if (itemStatus) {
                  itemStatus.textContent = `Success. Archived to ${job.archivedPath || 'old/'}`;
              }
              if (badgeEl) {
                  badgeEl.textContent = 'Onboarded';
                  badgeEl.classList.remove('api-v4-result-badge-success', 'api-v4-result-badge-error');
                  badgeEl.classList.add('api-v4-result-badge-neutral');
              }
              loadActivationCramKeys();
          } else {
              if (itemStatus) {
                  itemStatus.textContent = `Failed. Exit code: ${job.exitCode}`;
              }
              if (badgeEl) {
                  badgeEl.textContent = 'error';
                  badgeEl.classList.remove('api-v4-result-badge-success', 'api-v4-result-badge-neutral');
                  badgeEl.classList.add('api-v4-result-badge-error');
              }
          }

          if (button) {
              button.disabled = false;
          }
          return true;
      };

      const runPoll = async function () {
          try {
              const response = await fetch(`/api/cramkeys/activate/status/${encodeURIComponent(jobId)}`);
              const job = await response.json();
              if (!response.ok || job.error) {
                  throw new Error(job.error || 'Failed to load activation status');
              }

              const finished = renderJob(job);
              if (finished) {
                  clearInterval(activationPollers.get(jobId));
                  activationPollers.delete(jobId);
              }
          } catch (err) {
              if (itemStatus) {
                  itemStatus.textContent = `Status check failed: ${err.message}`;
              }
              if (badgeEl) {
                  badgeEl.textContent = 'error';
                  badgeEl.classList.remove('api-v4-result-badge-success', 'api-v4-result-badge-neutral');
                  badgeEl.classList.add('api-v4-result-badge-error');
              }
              if (button) {
                  button.disabled = false;
              }
              clearInterval(activationPollers.get(jobId));
              activationPollers.delete(jobId);
          }
      };

      await runPoll();
      const intervalId = setInterval(runPoll, 1000);
      activationPollers.set(jobId, intervalId);
  }

  async function openAtSignInApkamManagement(atSign) {
      const normalized = atSign.startsWith('@') ? atSign : `@${atSign}`;
      const response = await fetch('/api/select-atsign', {
          method: 'POST',
          headers: {
              'Content-Type': 'application/json'
          },
          body: JSON.stringify({ atsign: normalized })
      });
      const data = await response.json();
      if (!response.ok || data.error) {
          throw new Error(data.error || 'Failed to open atSign');
      }

      if (typeof switchTab === 'function' && typeof loadEnrollments === 'function') {
          switchTab('apkam-management');
          loadEnrollments();
          return;
      }

      window.location.href = '/';
  }

  async function persistSuccessfulTransaction(endpoint, payload) {
      const allowed = {
          'register-atsign-v4': 'register-atsign',
          'register-atsign-v3': 'register-atsign',
          'register-atsign-v4-batch': 'register-atsign',
          'reset-atsign': 'reset-atsign',
          'delete-atsigns': 'delete-atsign'
      };

      const type = allowed[endpoint.id];
      if (!type) {
          return null;
      }

      const responseBody = payload && payload.response ? payload.response.body : null;
      if (!responseBody || responseBody.status !== 'success') {
          return null;
      }

      const response = await fetch('/api/transactions/save', {
          method: 'POST',
          headers: {
              'Content-Type': 'application/json'
          },
          body: JSON.stringify({
              type,
              transaction: payload
          })
      });

          const data = await response.json();
          if (!response.ok || data.error) {
              throw new Error(data.error || 'Failed to save register transaction');
          }

      if (data.cramKeyPath) {
          const saveStatus = document.getElementById('api-v4-save-status');
          if (saveStatus) {
              saveStatus.textContent = `Saved CRAM key file: ${data.cramKeyPath}`;
          }
      }

      return data.path || null;
  }

  function normalizeAtSignForRequest(value) {
      return value.startsWith('@') ? value.substring(1) : value;
  }

  function buildRequestDetails(endpoint, env, apiKey, payload) {
      const url = buildUrl(endpoint, env, payload);
      const headers = {
          'Content-Type': 'application/json'
      };

      if (endpoint.authRequired && !apiKey) {
          throw new Error('Authorization API key is required for this call');
      }

      if (apiKey) {
          headers.authorization = apiKey;
      }

      const requestInit = {
          method: endpoint.method,
          headers
      };

      if (endpoint.method !== 'GET') {
          requestInit.body = JSON.stringify(payload);
      }

      return { url, headers, requestInit };
  }

  async function ensureInternalDartApiReady() {
      let response;
      try {
          response = await fetch('/api/health', {
              cache: 'no-store',
              headers: {
                  'Cache-Control': 'no-cache, no-store, must-revalidate',
                  Pragma: 'no-cache'
              }
          });
      } catch (_) {
          throw new Error('Internal Dart service is unreachable');
      }

      let payload = null;
      try {
          payload = await response.json();
      } catch (_) {
          payload = null;
      }

      if (!response.ok || !payload || payload.status !== 'ok') {
          throw new Error(payload && payload.error ? payload.error : 'Internal Dart service is unavailable');
      }
  }

  async function executeApiRequest(endpoint, env, apiKey, payload) {
      await ensureInternalDartApiReady();

      const controller = new AbortController();
      const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
      const details = buildRequestDetails(endpoint, env, apiKey, payload);
      details.requestInit.signal = controller.signal;

      let response;
      try {
          response = await fetch(details.url, details.requestInit);
      } finally {
          clearTimeout(timeoutId);
      }

      const rawText = await response.text();
      let parsedBody = rawText;
      try {
          parsedBody = rawText ? JSON.parse(rawText) : '';
      } catch (_) {}

      const responseHeaders = {};
      response.headers.forEach((value, key) => {
          responseHeaders[key] = value;
      });

      return {
          response,
          parsedBody,
          resultPayload: {
              request: {
                  environment: env,
                  url: details.url,
                  method: endpoint.method,
                  headers: details.headers,
                  body: details.requestInit.body || null
              },
              response: {
                  status: response.status,
                  statusText: response.statusText,
                  ok: response.ok,
                  headers: responseHeaders,
                  body: parsedBody
              }
          }
      };
  }

  async function sendBatchRegisterRequest(row, endpoint) {
      const statusOutput = row.querySelector('[data-role="status"]');
      const output = row.querySelector('[data-role="output"]');
      const result = row.querySelector('[data-role="result"]');
      const batchResults = row.querySelector('[data-role="batch-results"]');
      const button = row.querySelector('[data-role="send"]');
      const warning = row.querySelector('[data-role="warning"]');
      const startedAt = Date.now();
      statusOutput.textContent = 'Preparing batch request...';
      button.disabled = true;
      output.textContent = '';
      if (result) {
          result.hidden = true;
          result.innerHTML = '';
      }
      if (batchResults) {
          batchResults.hidden = true;
          batchResults.innerHTML = '';
      }
      if (warning) {
          warning.hidden = true;
          warning.textContent = '';
      }

      try {
          const env = row.querySelector('[data-role="env"]').value;
          const apiKey = row.querySelector('[data-role="api-key"]').value.trim();
          const payload = collectPayload(row, endpoint);
          const atSigns = (payload.atSigns || []).filter(Boolean);

          if (atSigns.length === 0) {
              throw new Error('Enter at least one Atsign for batch registration');
          }

          const preview = atSigns.join('\n');
          const confirmed = window.confirm(`Are you really sure you want to register this list of Atsigns?\n\n${preview}`);
          if (!confirmed) {
              statusOutput.textContent = 'Batch request cancelled';
              return;
          }

          if (batchResults) {
              batchResults.hidden = false;
              batchResults.innerHTML = atSigns
                  .map((atSign) => `
                      <div class="api-v4-batch-item" data-batch-atsign="${escapeHtml(atSign)}">
                          <div class="api-v4-batch-item-head">
                              <span class="api-v4-batch-atsign">${escapeHtml(atSign)}</span>
                              <span class="api-v4-batch-state">Queued</span>
                          </div>
                          <div class="api-v4-batch-message"></div>
                          <div class="api-v4-batch-cramkey" hidden></div>
                      </div>
                  `)
                  .join('');
          }

          const allResults = [];
          let successCount = 0;

          for (let index = 0; index < atSigns.length; index += 1) {
              const atSign = atSigns[index];
              statusOutput.textContent = `Processing ${index + 1} of ${atSigns.length}...`;
              const item = batchResults
                  ? Array.from(batchResults.querySelectorAll('[data-batch-atsign]')).find((element) => element.getAttribute('data-batch-atsign') === atSign)
                  : null;
              if (item) {
                  item.querySelector('.api-v4-batch-state').textContent = 'Sending...';
              }

              const itemPayload = {
                  atSign,
                  operation: 'register'
              };
              if (payload.startAtServer) {
                  itemPayload.startAtServer = payload.startAtServer;
              }

              try {
                  const executed = await executeApiRequest(endpoint, env, apiKey, itemPayload);
                  allResults.push(executed.resultPayload);
                  const transactionPath = await persistSuccessfulTransaction(endpoint, executed.resultPayload);
                  const apiStatus = executed.parsedBody && executed.parsedBody.status === 'success' ? 'success' : 'error';

                  if (item) {
                      item.classList.add(`api-v4-batch-item-${apiStatus}`);
                      item.querySelector('.api-v4-batch-state').textContent = apiStatus === 'success' ? 'Success' : 'Error';
                      item.querySelector('.api-v4-batch-message').textContent = executed.parsedBody.message || (apiStatus === 'success' ? 'Registered successfully' : 'Request completed');
                      if (executed.parsedBody.cramkey) {
                          const cramkeyEl = item.querySelector('.api-v4-batch-cramkey');
                          cramkeyEl.hidden = false;
                          cramkeyEl.innerHTML = `
                              <code>${escapeHtml(executed.parsedBody.cramkey)}</code>
                              <button type="button" class="api-v4-secondary" data-copy-cramkey="${escapeHtml(executed.parsedBody.cramkey)}">Copy</button>
                          `;
                      }
                      if (transactionPath) {
                          item.querySelector('.api-v4-batch-message').textContent += ` | Saved: ${transactionPath}`;
                      }
                  }

                  if (apiStatus === 'success') {
                      successCount += 1;
                  }
              } catch (err) {
                  allResults.push({ atSign, error: err && err.message ? err.message : String(err) });
                  if (item) {
                      item.classList.add('api-v4-batch-item-error');
                      item.querySelector('.api-v4-batch-state').textContent = 'Error';
                      item.querySelector('.api-v4-batch-message').textContent = err && err.message ? err.message : String(err);
                  }
              }
          }

          if (batchResults) {
              batchResults.querySelectorAll('[data-copy-cramkey]').forEach((copyBtn) => {
                  copyBtn.addEventListener('click', async function () {
                      const value = copyBtn.getAttribute('data-copy-cramkey') || '';
                      await navigator.clipboard.writeText(value);
                      copyBtn.textContent = 'Copied';
                      setTimeout(() => {
                          copyBtn.textContent = 'Copy';
                      }, 1500);
                  });
              });
          }

          const elapsed = Date.now() - startedAt;
          statusOutput.textContent = `Batch completed: ${successCount}/${atSigns.length} succeeded (${elapsed} ms). One transaction file was created per attempted Atsign.`;
          output.textContent = JSON.stringify(allResults, null, 2);
      } catch (err) {
          const elapsed = Date.now() - startedAt;
          if (err && err.name === 'AbortError') {
              statusOutput.textContent = `Batch timed out after ${REQUEST_TIMEOUT_MS / 1000} seconds`;
              output.textContent = 'The batch request exceeded the 15 second timeout and was cancelled.';
          } else {
              statusOutput.textContent = `Batch failed (${elapsed} ms)`;
              output.textContent = err && err.stack ? err.stack : String(err);
          }
      } finally {
          button.disabled = false;
      }
  }

  function normalizeAtSignKey(value) {
      return String(value || '').trim().replace(/^@/, '').toLowerCase();
  }

  function sameAtSign(left, right) {
      return normalizeAtSignKey(left) === normalizeAtSignKey(right);
  }

  function extractAtSignNames(value) {
      if (!Array.isArray(value)) {
          return [];
      }

      return value
          .map((item) => {
              if (item && typeof item === 'object') {
                  return String(item.atSign || item.atsign || item.name || '');
              }
              return String(item || '');
          })
          .map((item) => item.trim())
          .filter(Boolean);
  }

  // The registrar reports a generic top-level message and puts the real
  // reason on each entry of failed/skippedatSigns, so keep those by Atsign.
  function extractAtSignMessages(value) {
      const messages = new Map();
      if (!Array.isArray(value)) {
          return messages;
      }

      value.forEach((item) => {
          if (!item || typeof item !== 'object') {
              return;
          }
          const name = normalizeAtSignKey(item.atSign || item.atsign || item.name || '');
          const message = String(item.message || item.reason || '').trim();
          if (name && message) {
              messages.set(name, message);
          }
      });
      return messages;
  }

  function renderDeleteItems(container, atSigns) {
      if (!container) return;

      container.hidden = false;
      container.innerHTML = atSigns
          .map((atSign) => `
              <div class="api-v4-batch-item" data-batch-atsign="${escapeHtml(atSign)}">
                  <div class="api-v4-batch-item-head">
                      <span class="api-v4-batch-atsign">${escapeHtml(atSign)}</span>
                      <span class="api-v4-batch-state">Queued</span>
                  </div>
                  <div class="api-v4-batch-message"></div>
              </div>
          `)
          .join('');
  }

  function updateDeleteItem(container, atSign, state, message) {
      if (!container) return;

      const item = Array.from(container.querySelectorAll('[data-batch-atsign]'))
          .find((element) => sameAtSign(element.getAttribute('data-batch-atsign'), atSign));
      if (!item) return;

      item.classList.add(state === 'Deleted' ? 'api-v4-batch-item-success' : 'api-v4-batch-item-error');
      item.querySelector('.api-v4-batch-state').textContent = state;
      item.querySelector('.api-v4-batch-message').textContent = message;
  }

  // Deleting an Atsign destroys its atServer, which leaves any local keys for
  // it useless. We report them rather than removing them: a local .atKeys file
  // may be an enrollment copy rather than the primary keys, and it is matched
  // only by name, so it may well belong to a different environment.
  async function warnAboutStaleLocalKeys(deletedAtSigns, warning) {
      if (!warning) return;

      let local;
      try {
          const response = await fetch('/api/atsigns');
          const data = await response.json();
          if (!response.ok || data.error) {
              throw new Error(data.error || 'Failed to list local Atsigns');
          }
          local = (data.atsigns || []).filter((entry) =>
              deletedAtSigns.some((atSign) => sameAtSign(entry.name, atSign)));
      } catch (_) {
          return;
      }

      if (local.length === 0) return;

      const services = local.reduce((total, entry) => total + (entry.activeAutoApprovalCount || 0), 0);
      warning.textContent = `Local keys were found for ${local.map((entry) => entry.name).join(', ')}: `
          + `${local.map((entry) => entry.path).join(', ')}. Their atServers have just been deleted, so these `
          + 'keys are stale and we advise deleting them if they are the corresponding atKeys. This console '
          + 'leaves them in place because a local key file may be an enrollment copy, or may belong to an '
          + 'Atsign of the same name in another environment.'
          + (services > 0 ? ` ${services} auto approval service(s) are still running against them.` : '');
      warning.hidden = false;
  }

  async function sendDeleteAtSignsRequest(row, endpoint) {
      const statusOutput = row.querySelector('[data-role="status"]');
      const output = row.querySelector('[data-role="output"]');
      const batchResults = row.querySelector('[data-role="batch-results"]');
      const button = row.querySelector('[data-role="send"]');
      const warning = row.querySelector('[data-role="warning"]');
      const startedAt = Date.now();
      const executedPayloads = [];

      clearRowOutput(row);
      statusOutput.textContent = 'Preparing delete request...';
      button.disabled = true;

      try {
          const env = row.querySelector('[data-role="env"]').value;
          const apiKey = row.querySelector('[data-role="api-key"]').value.trim();
          const payload = collectPayload(row, endpoint);
          const atSigns = (payload.atSigns || []).filter(Boolean);

          if (atSigns.length === 0) {
              throw new Error('Enter at least one Atsign to delete');
          }

          const preview = atSigns.join('\n');
          const confirmation = window.prompt(`Type DELETE to permanently delete ${atSigns.length} Atsign(s). This cannot be undone.\n\n${preview}`, '');
          if (confirmation === null) {
              statusOutput.textContent = 'Delete cancelled';
              output.textContent = 'Delete request cancelled before sending.';
              return;
          }

          if (confirmation.trim() !== 'DELETE') {
              throw new Error('Confirmation did not match. Type exactly: DELETE');
          }

          renderDeleteItems(batchResults, atSigns);

          statusOutput.textContent = 'Requesting delete token...';
          const tokenExecuted = await executeApiRequest(endpoint, env, apiKey, {
              atSigns,
              operation: 'deletetoken'
          });
          executedPayloads.push(tokenExecuted.resultPayload);

          const tokenBody = tokenExecuted.parsedBody && typeof tokenExecuted.parsedBody === 'object' ? tokenExecuted.parsedBody : {};
          const tokenData = tokenBody.data && typeof tokenBody.data === 'object' ? tokenBody.data : {};
          const skipped = extractAtSignNames(tokenData.skippedatSigns);
          const skippedMessages = extractAtSignMessages(tokenData.skippedatSigns);
          const token = tokenData.token ? String(tokenData.token) : '';

          skipped.forEach((atSign) => {
              updateDeleteItem(batchResults, atSign, 'Skipped',
                  skippedMessages.get(normalizeAtSignKey(atSign))
                  || 'The Authorization API Key did not create this Atsign, so it cannot delete it.');
          });

          const deletable = atSigns.filter((atSign) => !skipped.some((entry) => sameAtSign(entry, atSign)));
          if (deletable.length === 0) {
              throw new Error('The Authorization API Key did not create any of these Atsigns, so none of them can be deleted');
          }

          if (!token) {
              throw new Error(tokenBody.message || 'Registrar did not return a delete token');
          }

          statusOutput.textContent = `Deleting ${deletable.length} of ${atSigns.length}...`;
          const deleteExecuted = await executeApiRequest(endpoint, env, apiKey, {
              token,
              atSigns: deletable,
              operation: 'delete'
          });
          executedPayloads.push(deleteExecuted.resultPayload);

          const deleteBody = deleteExecuted.parsedBody && typeof deleteExecuted.parsedBody === 'object' ? deleteExecuted.parsedBody : {};
          const deleteData = deleteBody.data && typeof deleteBody.data === 'object' ? deleteBody.data : {};
          const failed = extractAtSignNames(deleteData.failed);
          // The spec also allows ineligible Atsigns to come back as skipped
          // on this call, though the registrar has only been seen using failed.
          const skippedOnDelete = extractAtSignNames(deleteData.skippedatSigns);
          const reasons = new Map([
              ...extractAtSignMessages(deleteData.failed),
              ...extractAtSignMessages(deleteData.skippedatSigns)
          ]);
          const deleted = Array.isArray(deleteData.deleted)
              ? extractAtSignNames(deleteData.deleted)
              : (deleteBody.status === 'success'
                  ? deletable.filter((atSign) => !failed.some((entry) => sameAtSign(entry, atSign)))
                  : []);

          deleted.forEach((atSign) => {
              updateDeleteItem(batchResults, atSign, 'Deleted', 'Deleted from the registrar.');
          });

          const unresolved = deletable.filter((atSign) => !deleted.some((entry) => sameAtSign(entry, atSign)));
          const failureReasons = [];
          unresolved.forEach((atSign) => {
              const reason = reasons.get(normalizeAtSignKey(atSign))
                  || deleteBody.message
                  || 'The registrar did not report this Atsign as deleted.';
              if (!failureReasons.includes(reason)) {
                  failureReasons.push(reason);
              }
              const state = skippedOnDelete.some((entry) => sameAtSign(entry, atSign)) ? 'Skipped' : 'Failed';
              updateDeleteItem(batchResults, atSign, state, reason);
          });

          output.textContent = JSON.stringify(executedPayloads, null, 2);

          let transactionPath = null;
          try {
              transactionPath = await persistSuccessfulTransaction(endpoint, deleteExecuted.resultPayload);
          } catch (persistError) {
              if (warning) {
                  warning.textContent = `Warning: the delete request completed, but the transaction file was not saved: ${persistError.message}`;
                  warning.hidden = false;
              }
          }

          if (deleted.length > 0) {
              await warnAboutStaleLocalKeys(deleted, warning);
          }

          const elapsed = Date.now() - startedAt;
          statusOutput.textContent = `Deleted ${deleted.length}/${atSigns.length} (${elapsed} ms).`
              + (skipped.length ? ` Skipped ${skipped.length}.` : '')
              + (unresolved.length ? ` Failed ${unresolved.length}.` : '')
              // One shared reason is the common case, and it is the only thing
              // worth reading; per-Atsign reasons stay on the rows below.
              + (failureReasons.length === 1 ? ` ${failureReasons[0]}` : '')
              + (transactionPath ? ` Transaction saved to: ${transactionPath}` : '');
      } catch (err) {
          const elapsed = Date.now() - startedAt;
          const details = executedPayloads.length ? `${JSON.stringify(executedPayloads, null, 2)}\n\n` : '';
          if (err && err.name === 'AbortError') {
              statusOutput.textContent = `Request timed out after ${REQUEST_TIMEOUT_MS / 1000} seconds`;
              output.textContent = `${details}The request exceeded the 15 second timeout and was cancelled.`;
          } else {
              statusOutput.textContent = `Delete failed (${elapsed} ms)`;
              output.textContent = details + (err && err.stack ? err.stack : String(err));
          }
      } finally {
          button.disabled = false;
      }
  }

  async function sendRequest(row, endpoint) {
      const statusOutput = row.querySelector('[data-role="status"]');
      const output = row.querySelector('[data-role="output"]');
      const button = row.querySelector('[data-role="send"]');
      const startedAt = Date.now();
      statusOutput.textContent = 'Sending request...';
      button.disabled = true;

      try {
          const env = row.querySelector('[data-role="env"]').value;
          const apiKey = row.querySelector('[data-role="api-key"]').value.trim();
          const payload = collectPayload(row, endpoint);

          if (endpoint.id === 'reset-atsign') {
              const atSignToReset = String(payload.atSign || '').trim();
              const confirmation = window.prompt(`Type the exact Atsign to reset: ${atSignToReset}`, '');
              if (confirmation === null) {
                  statusOutput.textContent = 'Reset cancelled';
                  output.textContent = 'Reset request cancelled before sending.';
                  return;
              }

              if (confirmation.trim() !== atSignToReset) {
                  throw new Error(`Confirmation did not match. Type exactly: ${atSignToReset}`);
              }
          }

          const executed = await executeApiRequest(endpoint, env, apiKey, payload);
          const parsedBody = executed.parsedBody;
          const resultPayload = executed.resultPayload;

          let transactionPath = null;
          try {
              transactionPath = await persistSuccessfulTransaction(endpoint, resultPayload);
          } catch (persistError) {
              const warning = row.querySelector('[data-role="warning"]');
              if (warning) {
                  warning.textContent = `Warning: request completed, but transaction file was not saved: ${persistError.message}`;
                  warning.hidden = false;
              }
          }

          const elapsed = Date.now() - startedAt;
          statusOutput.textContent = `${executed.response.status} ${executed.response.statusText} (${elapsed} ms)`;
          renderPrimarySummary(row, endpoint, parsedBody);
          if (transactionPath) {
              const result = row.querySelector('[data-role="result"]');
              if (result && !result.hidden) {
                  result.innerHTML += `<div class="api-v4-transaction-path">Transaction saved to: <code>${escapeHtml(transactionPath)}</code></div>`;
              }
          }
          output.textContent = JSON.stringify(resultPayload, null, 2);
      } catch (err) {
          const elapsed = Date.now() - startedAt;
          if (err && err.name === 'AbortError') {
              statusOutput.textContent = `Request timed out after ${REQUEST_TIMEOUT_MS / 1000} seconds`;
              output.textContent = 'The request exceeded the 15 second timeout and was cancelled.';
          } else {
              statusOutput.textContent = `Request failed (${elapsed} ms)`;
              output.textContent = err && err.stack ? err.stack : String(err);
          }
          renderPrimarySummary(row, endpoint, null);
      } finally {
          button.disabled = false;
      }
  }

  function clearRowOutput(row) {
      const result = row.querySelector('[data-role="result"]');
      if (result) {
          result.hidden = true;
          result.innerHTML = '';
      }
      const batchResults = row.querySelector('[data-role="batch-results"]');
      if (batchResults) {
          batchResults.hidden = true;
          batchResults.innerHTML = '';
      }
      row.querySelector('[data-role="status"]').textContent = 'Ready';
      row.querySelector('[data-role="output"]').textContent = '';
      const warning = row.querySelector('[data-role="warning"]');
      if (warning) {
          warning.hidden = true;
          warning.textContent = '';
      }
  }

  function bindRow(root, endpoint) {
      const row = root.querySelector(`[data-endpoint-id="${endpoint.id}"]`);
      const toggle = row.querySelector('[data-role="toggle"]');
      const body = row.querySelector('[data-role="body"]');
      row.querySelector('[data-role="send"]').addEventListener('click', function () {
          if (endpoint.type === 'batch-register') {
              sendBatchRegisterRequest(row, endpoint);
          } else if (endpoint.type === 'delete-atsigns') {
              sendDeleteAtSignsRequest(row, endpoint);
          } else {
              sendRequest(row, endpoint);
          }
      });
      row.querySelector('[data-role="clear"]').addEventListener('click', function () {
          clearRowOutput(row);
      });
      toggle.addEventListener('click', function () {
          const expanded = toggle.getAttribute('aria-expanded') === 'true';
          toggle.setAttribute('aria-expanded', expanded ? 'false' : 'true');
          body.hidden = expanded;
      });
  }

  function syncApiKeyInputs(root, value, visible) {
      if (!root) return;

      root.querySelectorAll('[data-role="api-key"]').forEach((input) => {
          input.value = value;
          input.type = visible ? 'text' : 'password';
      });
  }

  async function loadSavedConfig(statusEl, configFields, root, isVisible) {
      try {
          const response = await fetch('/api/config');
          const data = await response.json();
          if (!response.ok || data.error) {
              throw new Error(data.error || 'Failed to load config');
          }

          configFields.apiKey.value = data.registrarApiKey || '';
          configFields.prefix.value = data.atsignPrefix || '';
          configFields.postfix.value = data.atsignPostfix || '';
          syncApiKeyInputs(root, configFields.apiKey.value, isVisible);

          if (statusEl) {
              statusEl.textContent = 'Loaded config from ~/.atsign/at_activate_web/config.json';
          }
      } catch (err) {
          if (statusEl) {
              statusEl.textContent = `Load failed: ${err.message}`;
          }
      }
  }

  function bindConfigPanel(root) {
      const globalApiKey = document.getElementById('api-v4-global-api-key');
      const atsignPrefix = document.getElementById('api-v4-atsign-prefix');
      const atsignPostfix = document.getElementById('api-v4-atsign-postfix');
      const toggleButton = document.getElementById('api-v4-toggle-api-key');
      const copyButton = document.getElementById('api-v4-copy-api-key');
      const saveButton = document.getElementById('api-v4-save-api-key');
      const refreshButton = document.getElementById('api-v4-refresh-config');
      const openFolderButton = document.getElementById('api-v4-open-config-folder');
      const saveStatus = document.getElementById('api-v4-save-status');
      if (!globalApiKey || !atsignPrefix || !atsignPostfix) return;

      let isVisible = false;
      const configFields = {
          apiKey: globalApiKey,
          prefix: atsignPrefix,
          postfix: atsignPostfix
      };

      const sync = function () {
          syncApiKeyInputs(root, globalApiKey.value, isVisible);
      };

      globalApiKey.addEventListener('input', sync);
      if (toggleButton) {
          toggleButton.addEventListener('click', function () {
              isVisible = !isVisible;
              globalApiKey.type = isVisible ? 'text' : 'password';
              toggleButton.textContent = isVisible ? 'Hide' : 'Show';
              sync();
          });
      }
      if (copyButton) {
          copyButton.addEventListener('click', async function () {
              try {
                  await navigator.clipboard.writeText(globalApiKey.value);
                  if (saveStatus) saveStatus.textContent = 'API key copied to clipboard';
              } catch (_) {
                  if (saveStatus) saveStatus.textContent = 'Copy failed';
              }
          });
      }
      if (saveButton) {
          saveButton.addEventListener('click', async function () {
              saveButton.disabled = true;
              if (saveStatus) saveStatus.textContent = 'Saving...';
              try {
                  const response = await fetch('/api/config', {
                      method: 'POST',
                      headers: {
                          'Content-Type': 'application/json'
                      },
                      body: JSON.stringify({
                          registrarApiKey: globalApiKey.value,
                          atsignPrefix: atsignPrefix.value,
                          atsignPostfix: atsignPostfix.value
                      })
                  });
                  const data = await response.json();
                  if (!response.ok || data.error) {
                      throw new Error(data.error || 'Failed to save config');
                  }
                  if (saveStatus) saveStatus.textContent = 'Saved to ~/.atsign/at_activate_web/config.json';
              } catch (err) {
                  if (saveStatus) saveStatus.textContent = `Save failed: ${err.message}`;
              } finally {
                  saveButton.disabled = false;
              }
          });
      }

      if (refreshButton) {
          refreshButton.addEventListener('click', async function () {
              refreshButton.disabled = true;
              if (saveStatus) saveStatus.textContent = 'Refreshing...';
              try {
                  await loadSavedConfig(saveStatus, configFields, root, isVisible);
              } finally {
                  refreshButton.disabled = false;
              }
          });
      }

      if (openFolderButton) {
          openFolderButton.addEventListener('click', async function () {
              openFolderButton.disabled = true;
              if (saveStatus) saveStatus.textContent = 'Opening config folder...';
              try {
                  const response = await fetch('/api/config/open-folder', {
                      method: 'POST'
                  });
                  const data = await response.json();
                  if (!response.ok || data.error) {
                      throw new Error(data.error || 'Failed to open config folder');
                  }
                  if (saveStatus) saveStatus.textContent = `Opened config folder: ${data.path}`;
              } catch (err) {
                  if (saveStatus) saveStatus.textContent = `Open failed: ${err.message}`;
              } finally {
                  openFolderButton.disabled = false;
              }
          });
      }

      globalApiKey.type = 'password';
      sync();
      loadSavedConfig(saveStatus, configFields, root, isVisible);
  }

  async function initializeApiV4Console() {
      const root = document.getElementById('api-v4-rows');
      if (!root) return;
      root.innerHTML = PRIMARY_ENDPOINTS.map(endpointMarkup).join('') + otherSectionMarkup();
      ALL_ENDPOINTS.forEach((endpoint) => bindRow(root, endpoint));
      bindConfigPanel(root);
      window.loadApiV4Transactions = loadApiV4Transactions;
      window.openTransactionsFolder = openTransactionsFolder;
      window.loadActivationCramKeys = loadActivationCramKeys;
      window.openCramKeysFolder = openCramKeysFolder;
  }

  if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', initializeApiV4Console);
  } else {
      initializeApiV4Console();
  }
})();
''';