(function () {
  // shell: www-only codec between collectSessionData()'s JS shape and real
  // .sqlite3 bytes, via sql.js (SQLite compiled to WASM - vendored verbatim,
  // not our own WASM code). Schema/read-write order below is a hand-copied
  // mirror of src-tauri/src/main.rs's SESSION_SCHEMA_SQL/write_session_data/
  // read_session_data - keep the two in sync if the Rust schema ever changes.
  // Vendored from https://unpkg.com/sql.js@1.14.1/dist/ - bump the version in
  // both this comment and js/new-ui/vendor/sql-js/ together when upgrading.
  var VENDOR_BASE = "js/new-ui/vendor/sql-js/";

  var SESSION_SCHEMA_SQL = [
    "CREATE TABLE IF NOT EXISTS scan_results (",
    "  id INTEGER PRIMARY KEY AUTOINCREMENT,",
    "  ip TEXT NOT NULL, ping TEXT NOT NULL, hostname TEXT NOT NULL,",
    "  flag TEXT NOT NULL, isp TEXT NOT NULL, as_info TEXT NOT NULL,",
    "  device_identification TEXT NOT NULL, city TEXT NOT NULL DEFAULT '',",
    "  country_code TEXT NOT NULL DEFAULT '', lat REAL, lon REAL,",
    "  status TEXT NOT NULL, status_class TEXT NOT NULL",
    ");",
    "CREATE TABLE IF NOT EXISTS scan_result_ports (",
    "  id INTEGER PRIMARY KEY AUTOINCREMENT,",
    "  result_id INTEGER NOT NULL REFERENCES scan_results(id) ON DELETE CASCADE,",
    "  port INTEGER NOT NULL,",
    "  protocol TEXT NOT NULL DEFAULT 'TCP',",
    "  status TEXT NOT NULL DEFAULT 'open',",
    "  service TEXT NOT NULL DEFAULT '',",
    "  ping TEXT NOT NULL DEFAULT '-'",
    ");",
    "CREATE TABLE IF NOT EXISTS ip_library_entries (",
    "  id INTEGER PRIMARY KEY AUTOINCREMENT, country_code TEXT NOT NULL, cidr TEXT NOT NULL",
    ");",
    "CREATE TABLE IF NOT EXISTS ip_library_meta (",
    "  id INTEGER PRIMARY KEY CHECK (id = 1), updated_at TEXT NOT NULL",
    ");",
    "CREATE TABLE IF NOT EXISTS port_presets (",
    "  id TEXT PRIMARY KEY, emoji TEXT NOT NULL, name TEXT NOT NULL, ports TEXT NOT NULL,",
    "  is_default INTEGER NOT NULL DEFAULT 0",
    ");",
    "CREATE TABLE IF NOT EXISTS scan_defaults (",
    "  id INTEGER PRIMARY KEY CHECK (id = 1), timeout_ms INTEGER NOT NULL, concurrency INTEGER NOT NULL",
    ");",
    "CREATE TABLE IF NOT EXISTS scan_progress (",
    "  id INTEGER PRIMARY KEY CHECK (id = 1), state TEXT NOT NULL, processed INTEGER NOT NULL,",
    "  total INTEGER NOT NULL, found INTEGER NOT NULL",
    ");",
    "CREATE TABLE IF NOT EXISTS agent_profiles (",
    "  id TEXT PRIMARY KEY, name TEXT NOT NULL, nickname TEXT NOT NULL DEFAULT '',",
    "  email TEXT NOT NULL DEFAULT '', login TEXT NOT NULL DEFAULT '',",
    "  password TEXT NOT NULL DEFAULT '', note TEXT NOT NULL DEFAULT ''",
    ");",
    "CREATE TABLE IF NOT EXISTS agent_profile_attachments (",
    "  id TEXT PRIMARY KEY,",
    "  profile_id TEXT NOT NULL REFERENCES agent_profiles(id) ON DELETE CASCADE,",
    "  filename TEXT NOT NULL, mime_type TEXT NOT NULL,",
    "  role TEXT NOT NULL DEFAULT 'file', data BLOB NOT NULL",
    ");",
    "CREATE TABLE IF NOT EXISTS agent_profile_services (",
    "  id TEXT PRIMARY KEY,",
    "  profile_id TEXT NOT NULL REFERENCES agent_profiles(id) ON DELETE CASCADE,",
    "  name TEXT NOT NULL DEFAULT ''",
    ");",
    "CREATE TABLE IF NOT EXISTS agent_profile_service_fields (",
    "  id TEXT PRIMARY KEY,",
    "  service_id TEXT NOT NULL REFERENCES agent_profile_services(id) ON DELETE CASCADE,",
    "  label TEXT NOT NULL DEFAULT '', type TEXT NOT NULL DEFAULT 'text',",
    "  value TEXT NOT NULL DEFAULT ''",
    ");",
    "CREATE TABLE IF NOT EXISTS session_layout_tabs (",
    "  id INTEGER PRIMARY KEY AUTOINCREMENT, section TEXT NOT NULL, tool TEXT NOT NULL,",
    "  is_active INTEGER NOT NULL DEFAULT 0",
    ");",
    "CREATE TABLE IF NOT EXISTS session_meta (",
    "  id INTEGER PRIMARY KEY CHECK (id = 1), saved_at TEXT NOT NULL, version INTEGER NOT NULL,",
    "  app_version TEXT NOT NULL DEFAULT ''",
    ");",
    "CREATE TABLE IF NOT EXISTS session_extensions (",
    "  id TEXT PRIMARY KEY, name TEXT NOT NULL, version TEXT NOT NULL, manifest_json TEXT NOT NULL",
    ");",
  ].join("\n");

  // Agent profile attachment bytes cross as base64 in the JS shape (same as
  // the Rust side's IPC boundary - see main.rs's own comment on this) but
  // need to be real bytes for sql.js's BLOB bind parameters / SELECT
  // results. This file has no shared-utils import, so these are duplicated
  // rather than pulled from session-runtime.js's identical pair - consistent
  // with SESSION_SCHEMA_SQL itself already being a hand-copied duplicate
  // between here and main.rs.
  function base64ToBytes(base64) {
    var binary = atob(base64 || "");
    var bytes = new Uint8Array(binary.length);
    for (var i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
    return bytes;
  }

  function bytesToBase64(bytes) {
    var chunkSize = 0x8000;
    var binary = "";
    for (var i = 0; i < bytes.length; i += chunkSize) {
      binary += String.fromCharCode.apply(null, bytes.subarray(i, i + chunkSize));
    }
    return btoa(binary);
  }

  var enginePromise = null;

  function injectScript(src) {
    return new Promise(function (resolve, reject) {
      var el = document.createElement("script");
      el.src = src;
      el.onload = function () { resolve(); };
      el.onerror = function () { reject(new Error("failed to load " + src)); };
      document.head.appendChild(el);
    });
  }

  function loadEngine() {
    if (enginePromise) return enginePromise;
    enginePromise = injectScript(VENDOR_BASE + "sql-wasm.js")
      .then(function () {
        if (typeof window.initSqlJs !== "function") {
          throw new Error("initSqlJs not available after loading sql-wasm.js");
        }
        return window.initSqlJs({ locateFile: function (f) { return VENDOR_BASE + f; } });
      })
      .catch(function (err) {
        enginePromise = null;
        throw err;
      });
    return enginePromise;
  }

  function encodeSessionData(data) {
    data = data || {};
    return loadEngine().then(function (SQL) {
      var db = new SQL.Database();
      try {
        db.run(SESSION_SCHEMA_SQL);

        var scanResults = Array.isArray(data.scanResults) ? data.scanResults : [];
        var insertResult = db.prepare(
          "INSERT INTO scan_results (id, ip, ping, hostname, flag, isp, as_info, device_identification, city, country_code, lat, lon, status, status_class) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
        );
        var insertPort = db.prepare("INSERT INTO scan_result_ports (result_id, port, protocol, status, service, ping) VALUES (?, ?, ?, ?, ?, ?)");
        scanResults.forEach(function (row, index) {
          row = row || {};
          var id = index + 1;
          insertResult.run([
            id,
            String(row.ip || ""),
            String(row.ping || ""),
            String(row.hostname || ""),
            String(row.flag || ""),
            String(row.isp || ""),
            String(row.as || ""),
            String(row.deviceIdentification || ""),
            String(row.city || ""),
            String(row.countryCode || ""),
            typeof row.lat === "number" ? row.lat : null,
            typeof row.lon === "number" ? row.lon : null,
            String(row.status || ""),
            String(row.statusClass || ""),
          ]);
          var ports = Array.isArray(row.ports) ? row.ports : [];
          ports.forEach(function (port) {
            if (port && typeof port === "object") {
              insertPort.run([id, Number(port.port) || 0, String(port.protocol || "TCP"), String(port.status || "open"), String(port.service || ""), String(port.ping || "-") || "-"]);
            } else {
              insertPort.run([id, Number(port) || 0, "TCP", "open", "", "-"]);
            }
          });
        });
        insertResult.free();
        insertPort.free();

        var ipLibrary = data.ipLibrary || {};
        var entries = Array.isArray(ipLibrary.entries) ? ipLibrary.entries : [];
        var insertEntry = db.prepare("INSERT INTO ip_library_entries (country_code, cidr) VALUES (?, ?)");
        entries.forEach(function (entry) {
          entry = entry || {};
          insertEntry.run([String(entry.countryCode || ""), String(entry.cidr || "")]);
        });
        insertEntry.free();
        db.run("INSERT INTO ip_library_meta (id, updated_at) VALUES (1, ?)", [String(ipLibrary.updatedAt || "")]);

        var presetsObj = data.presets || {};
        var presetsList = Array.isArray(presetsObj.presets) ? presetsObj.presets : [];
        var defaultPresetId = String(presetsObj.defaultPresetId || "");
        var insertPreset = db.prepare("INSERT INTO port_presets (id, emoji, name, ports, is_default) VALUES (?,?,?,?,?)");
        presetsList.forEach(function (preset) {
          preset = preset || {};
          var id = String(preset.id || "");
          insertPreset.run([
            id,
            String(preset.emoji || ""),
            String(preset.name || ""),
            String(preset.ports || ""),
            id === defaultPresetId ? 1 : 0,
          ]);
        });
        insertPreset.free();

        var agentProfilesObj = data.agentProfiles || {};
        var agentProfiles = Array.isArray(agentProfilesObj.profiles) ? agentProfilesObj.profiles : [];
        var agentAttachments = Array.isArray(agentProfilesObj.attachments) ? agentProfilesObj.attachments : [];
        var agentServices = Array.isArray(agentProfilesObj.services) ? agentProfilesObj.services : [];
        var agentFields = Array.isArray(agentProfilesObj.fields) ? agentProfilesObj.fields : [];
        var insertAgentProfile = db.prepare(
          "INSERT INTO agent_profiles (id, name, nickname, email, login, password, note) VALUES (?,?,?,?,?,?,?)"
        );
        agentProfiles.forEach(function (p) {
          p = p || {};
          insertAgentProfile.run([
            String(p.id || ""),
            String(p.name || ""),
            String(p.nickname || ""),
            String(p.email || ""),
            String(p.login || ""),
            String(p.password || ""),
            String(p.note || ""),
          ]);
        });
        insertAgentProfile.free();

        var insertAgentAttachment = db.prepare(
          "INSERT INTO agent_profile_attachments (id, profile_id, filename, mime_type, role, data) VALUES (?,?,?,?,?,?)"
        );
        agentAttachments.forEach(function (a) {
          a = a || {};
          insertAgentAttachment.run([
            String(a.id || ""),
            String(a.profileId || ""),
            String(a.filename || ""),
            String(a.mimeType || ""),
            String(a.role || "file"),
            base64ToBytes(a.dataBase64 || ""),
          ]);
        });
        insertAgentAttachment.free();

        var insertAgentService = db.prepare(
          "INSERT INTO agent_profile_services (id, profile_id, name) VALUES (?,?,?)"
        );
        agentServices.forEach(function (s) {
          s = s || {};
          insertAgentService.run([
            String(s.id || ""),
            String(s.profileId || ""),
            String(s.name || ""),
          ]);
        });
        insertAgentService.free();

        var insertAgentField = db.prepare(
          "INSERT INTO agent_profile_service_fields (id, service_id, label, type, value) VALUES (?,?,?,?,?)"
        );
        agentFields.forEach(function (f) {
          f = f || {};
          insertAgentField.run([
            String(f.id || ""),
            String(f.serviceId || ""),
            String(f.label || ""),
            String(f.type || "text"),
            String(f.value || ""),
          ]);
        });
        insertAgentField.free();

        var scanDefaults = data.scanDefaults || {};
        db.run("INSERT INTO scan_defaults (id, timeout_ms, concurrency) VALUES (1, ?, ?)", [
          Number(scanDefaults.timeoutMs) || 0,
          Number(scanDefaults.concurrency) || 0,
        ]);

        var scanProgress = data.scanProgress || {};
        db.run("INSERT INTO scan_progress (id, state, processed, total, found) VALUES (1, ?, ?, ?, ?)", [
          String(scanProgress.state || ""),
          Number(scanProgress.processed) || 0,
          Number(scanProgress.total) || 0,
          Number(scanProgress.found) || 0,
        ]);

        var layout = data.layout || {};
        var insertTab = db.prepare("INSERT INTO session_layout_tabs (section, tool, is_active) VALUES (?, ?, ?)");
        [["center", layout.center], ["left", layout.left], ["right", layout.right]].forEach(function (pair) {
          var sectionName = pair[0];
          var section = pair[1] || {};
          var open = Array.isArray(section.open) ? section.open : [];
          open.forEach(function (tool) {
            insertTab.run([sectionName, String(tool), section.active === tool ? 1 : 0]);
          });
        });
        insertTab.free();

        var extensions = Array.isArray(data.extensions) ? data.extensions : [];
        var insertExtension = db.prepare("INSERT INTO session_extensions (id, name, version, manifest_json) VALUES (?,?,?,?)");
        extensions.forEach(function (ext) {
          ext = ext || {};
          insertExtension.run([
            String(ext.id || ""),
            String(ext.name || ""),
            String(ext.version || ""),
            String(ext.manifestJson || ""),
          ]);
        });
        insertExtension.free();

        var appVersion = (window.NetReconNewUICore && window.NetReconNewUICore.APP_VERSION) || "";
        db.run("INSERT INTO session_meta (id, saved_at, version, app_version) VALUES (1, strftime('%Y-%m-%dT%H:%M:%fZ','now'), 1, ?)", [appVersion]);

        return db.export();
      } finally {
        db.close();
      }
    });
  }

  function decodeSessionBytes(bytes) {
    return loadEngine().then(function (SQL) {
      var db;
      try {
        db = new SQL.Database(bytes);
      } catch (_) {
        throw new Error("invalid sqlite file");
      }
      try {
        var scanResults = [];
        var indexById = {};
        // Older session files may be missing city/country_code/lat/lon
        // (added for the Location feature) - db.exec() throws on unknown
        // columns rather than returning NULL, so try the newest shape first
        // and fall back to the pre-feature shape on error, same discipline
        // as scan_result_ports' own multi-tier fallback below.
        var scanResultsHasLocation = true;
        var scanResultsRows;
        try {
          scanResultsRows = db.exec(
            "SELECT id, ip, ping, hostname, flag, isp, as_info, device_identification, city, country_code, lat, lon, status, status_class FROM scan_results ORDER BY id"
          );
        } catch (_) {
          scanResultsHasLocation = false;
          scanResultsRows = db.exec(
            "SELECT id, ip, ping, hostname, flag, isp, as_info, device_identification, status, status_class FROM scan_results ORDER BY id"
          );
        }
        if (scanResultsRows.length) {
          scanResultsRows[0].values.forEach(function (row, idx) {
            indexById[row[0]] = idx;
            if (scanResultsHasLocation) {
              scanResults.push({
                ip: String(row[1] || ""),
                ping: String(row[2] || ""),
                hostname: String(row[3] || ""),
                flag: String(row[4] || ""),
                isp: String(row[5] || ""),
                as: String(row[6] || ""),
                deviceIdentification: String(row[7] || ""),
                city: String(row[8] || ""),
                countryCode: String(row[9] || ""),
                lat: typeof row[10] === "number" ? row[10] : null,
                lon: typeof row[11] === "number" ? row[11] : null,
                status: String(row[12] || ""),
                statusClass: String(row[13] || ""),
                ports: [],
              });
            } else {
              scanResults.push({
                ip: String(row[1] || ""),
                ping: String(row[2] || ""),
                hostname: String(row[3] || ""),
                flag: String(row[4] || ""),
                isp: String(row[5] || ""),
                as: String(row[6] || ""),
                deviceIdentification: String(row[7] || ""),
                city: "",
                countryCode: "",
                lat: null,
                lon: null,
                status: String(row[8] || ""),
                statusClass: String(row[9] || ""),
                ports: [],
              });
            }
          });
        }

        // Older session files may be missing protocol/service/ping (added
        // incrementally over time) - fall back to defaults for whichever
        // columns aren't there yet, newest-shape first, rather than failing
        // to load the file.
        // Older session files may be missing protocol/status/service/ping
        // (added incrementally over time) - fall back to defaults for
        // whichever columns aren't there yet, newest-shape first.
        var portsRows, portsHasStatus = false;
        try {
          portsRows = db.exec("SELECT result_id, port, protocol, status, service, ping FROM scan_result_ports ORDER BY id");
          portsHasStatus = true;
        } catch (_) {
          try {
            portsRows = db.exec("SELECT result_id, port, protocol, service, ping FROM scan_result_ports ORDER BY id");
          } catch (__) {
            try {
              portsRows = db.exec("SELECT result_id, port, protocol, service FROM scan_result_ports ORDER BY id");
            } catch (___) {
              portsRows = db.exec("SELECT result_id, port FROM scan_result_ports ORDER BY id");
            }
          }
        }
        if (portsRows.length) {
          portsRows[0].values.forEach(function (row) {
            var idx = indexById[row[0]];
            if (idx === undefined || !scanResults[idx]) return;
            if (portsHasStatus) {
              scanResults[idx].ports.push({
                port: Number(row[1]) || 0,
                protocol: String(row[2] || "TCP"),
                status: String(row[3] || "open"),
                service: String(row[4] || ""),
                ping: String(row[5] || "-") || "-",
              });
            } else {
              scanResults[idx].ports.push({
                port: Number(row[1]) || 0,
                protocol: row.length > 2 ? String(row[2] || "TCP") : "TCP",
                status: "open",
                service: row.length > 3 ? String(row[3] || "") : "",
                ping: row.length > 4 ? (String(row[4] || "-") || "-") : "-",
              });
            }
          });
        }

        var scanProgress = { state: "", processed: 0, total: 0, found: 0 };
        var progressRows = db.exec("SELECT state, processed, total, found FROM scan_progress WHERE id = 1");
        if (progressRows.length && progressRows[0].values.length) {
          var pr = progressRows[0].values[0];
          scanProgress = { state: String(pr[0] || ""), processed: Number(pr[1]) || 0, total: Number(pr[2]) || 0, found: Number(pr[3]) || 0 };
        }

        var entries = [];
        var entriesRows = db.exec("SELECT country_code, cidr FROM ip_library_entries ORDER BY id");
        if (entriesRows.length) {
          entriesRows[0].values.forEach(function (row) {
            entries.push({ countryCode: String(row[0] || ""), cidr: String(row[1] || "") });
          });
        }

        var updatedAt = "";
        var metaRows = db.exec("SELECT updated_at FROM ip_library_meta WHERE id = 1");
        if (metaRows.length && metaRows[0].values.length) updatedAt = String(metaRows[0].values[0][0] || "");

        var presets = [];
        var defaultPresetId = "";
        var presetsRows = db.exec("SELECT id, emoji, name, ports, is_default FROM port_presets ORDER BY rowid");
        if (presetsRows.length) {
          presetsRows[0].values.forEach(function (row) {
            var id = String(row[0] || "");
            if (!!row[4]) defaultPresetId = id;
            presets.push({ id: id, emoji: String(row[1] || ""), name: String(row[2] || ""), ports: String(row[3] || "") });
          });
        }

        var agentProfilesList = [];
        var agentProfilesRows = db.exec("SELECT id, name, nickname, email, login, password, note FROM agent_profiles ORDER BY rowid");
        if (agentProfilesRows.length) {
          agentProfilesRows[0].values.forEach(function (row) {
            agentProfilesList.push({
              id: String(row[0] || ""),
              name: String(row[1] || ""),
              nickname: String(row[2] || ""),
              email: String(row[3] || ""),
              login: String(row[4] || ""),
              password: String(row[5] || ""),
              note: String(row[6] || ""),
            });
          });
        }

        var agentAttachmentsList = [];
        var agentAttachmentsRows = db.exec("SELECT id, profile_id, filename, mime_type, role, data FROM agent_profile_attachments ORDER BY rowid");
        if (agentAttachmentsRows.length) {
          agentAttachmentsRows[0].values.forEach(function (row) {
            agentAttachmentsList.push({
              id: String(row[0] || ""),
              profileId: String(row[1] || ""),
              filename: String(row[2] || ""),
              mimeType: String(row[3] || ""),
              role: String(row[4] || "file"),
              dataBase64: bytesToBase64(row[5] instanceof Uint8Array ? row[5] : new Uint8Array(0)),
            });
          });
        }

        var agentServicesList = [];
        var agentServicesRows = db.exec("SELECT id, profile_id, name FROM agent_profile_services ORDER BY rowid");
        if (agentServicesRows.length) {
          agentServicesRows[0].values.forEach(function (row) {
            agentServicesList.push({
              id: String(row[0] || ""),
              profileId: String(row[1] || ""),
              name: String(row[2] || ""),
            });
          });
        }

        var agentFieldsList = [];
        var agentFieldsRows = db.exec("SELECT id, service_id, label, type, value FROM agent_profile_service_fields ORDER BY rowid");
        if (agentFieldsRows.length) {
          agentFieldsRows[0].values.forEach(function (row) {
            agentFieldsList.push({
              id: String(row[0] || ""),
              serviceId: String(row[1] || ""),
              label: String(row[2] || ""),
              type: String(row[3] || "text"),
              value: String(row[4] || ""),
            });
          });
        }

        var scanDefaults = { timeoutMs: 0, concurrency: 0 };
        var defaultsRows = db.exec("SELECT timeout_ms, concurrency FROM scan_defaults WHERE id = 1");
        if (defaultsRows.length && defaultsRows[0].values.length) {
          var dr = defaultsRows[0].values[0];
          scanDefaults = { timeoutMs: Number(dr[0]) || 0, concurrency: Number(dr[1]) || 0 };
        }

        var layout = { center: { open: [], active: null }, left: { open: [], active: null }, right: { open: [], active: null } };
        var tabsRows = db.exec("SELECT section, tool, is_active FROM session_layout_tabs ORDER BY id");
        if (tabsRows.length) {
          tabsRows[0].values.forEach(function (row) {
            var bucket = layout[String(row[0] || "")];
            if (!bucket) return;
            var tool = String(row[1] || "");
            bucket.open.push(tool);
            if (!!row[2]) bucket.active = tool;
          });
        }

        // Older session files may be missing app_version (added for the
        // session versioning feature) - db.exec() throws on unknown columns,
        // so fall back to just saved_at on error, same discipline as every
        // other column added after this file format's original release.
        var meta = { savedAt: "", appVersion: "" };
        try {
          var metaWithVersionRows = db.exec("SELECT saved_at, app_version FROM session_meta WHERE id = 1");
          if (metaWithVersionRows.length && metaWithVersionRows[0].values.length) {
            var mv = metaWithVersionRows[0].values[0];
            meta = { savedAt: String(mv[0] || ""), appVersion: String(mv[1] || "") };
          }
        } catch (_) {
          try {
            var metaRowsLegacy = db.exec("SELECT saved_at FROM session_meta WHERE id = 1");
            if (metaRowsLegacy.length && metaRowsLegacy[0].values.length) {
              meta = { savedAt: String(metaRowsLegacy[0].values[0][0] || ""), appVersion: "" };
            }
          } catch (__) {}
        }

        // session_extensions is a brand new table - a session file saved
        // before this feature simply lacks it entirely, which db.exec()
        // throws on the same as a missing column would.
        var extensions = [];
        try {
          var extensionsRows = db.exec("SELECT id, name, version, manifest_json FROM session_extensions ORDER BY rowid");
          if (extensionsRows.length) {
            extensionsRows[0].values.forEach(function (row) {
              extensions.push({
                id: String(row[0] || ""),
                name: String(row[1] || ""),
                version: String(row[2] || ""),
                manifestJson: String(row[3] || ""),
              });
            });
          }
        } catch (_) {}

        return {
          scanResults: scanResults,
          scanProgress: scanProgress,
          ipLibrary: { entries: entries, updatedAt: updatedAt },
          presets: { defaultPresetId: defaultPresetId, presets: presets },
          agentProfiles: { profiles: agentProfilesList, attachments: agentAttachmentsList, services: agentServicesList, fields: agentFieldsList },
          scanDefaults: scanDefaults,
          layout: layout,
          meta: meta,
          extensions: extensions,
        };
      } finally {
        db.close();
      }
    });
  }

  function downloadBytes(filename, bytes) {
    var blob = new Blob([bytes], { type: "application/x-sqlite3" });
    var url = URL.createObjectURL(blob);
    var link = document.createElement("a");
    link.href = url;
    link.download = filename || "OSINT-session.sqlite3";
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    setTimeout(function () { URL.revokeObjectURL(url); }, 1000);
  }

  // Blob + <a download> always saves silently to the browser's configured
  // downloads folder - there's no way to make that prompt for a location,
  // that's a browser setting, not something a page can control. Chromium
  // browsers (Chrome/Edge, not Firefox/Safari) expose the File System Access
  // API instead, which DOES open a real native "Save As" folder picker
  // regardless of that setting - use it when available, otherwise fall back
  // to the plain download (still saves the file, just not folder-pickable).
  function saveBytesWithPicker(filename, bytes) {
    if (typeof window.showSaveFilePicker === "function") {
      // Only a user cancelling the picker itself ("AbortError") is a
      // non-failure. Any error from here on (picker refused, write() failed
      // mid-transfer, close() failed to finalize) is a real save failure and
      // must propagate - silently falling back to a Downloads-folder copy
      // would report success while the file was never written to the
      // location the user actually chose.
      return window.showSaveFilePicker({
        suggestedName: filename || "OSINT-session.sqlite3",
        types: [{ description: "SQLite session", accept: { "application/x-sqlite3": [".sqlite3"] } }],
      }).catch(function (err) {
        if (err && err.name === "AbortError") throw new Error("cancelled");
        throw err;
      }).then(function (handle) {
        return handle.createWritable().then(function (writable) {
          return writable.write(bytes).then(function () { return writable.close(); });
        }).then(function () { return handle.name; });
      });
    }
    downloadBytes(filename, bytes);
    return Promise.resolve(filename || "OSINT-session.sqlite3");
  }

  function pickFile() {
    return new Promise(function (resolve) {
      var input = document.createElement("input");
      input.type = "file";
      input.accept = ".sqlite3,.db";
      var settled = false;
      function settle(value) {
        if (settled) return;
        settled = true;
        resolve(value);
      }
      input.addEventListener("change", function () {
        settle(input.files && input.files[0] ? input.files[0] : null);
      });
      input.addEventListener("cancel", function () {
        settle(null);
      });
      input.click();
    });
  }

  function createSessionSqliteRuntime() {
    return {
      loadEngine: loadEngine,
      encodeSessionData: encodeSessionData,
      decodeSessionBytes: decodeSessionBytes,
      downloadBytes: downloadBytes,
      saveBytesWithPicker: saveBytesWithPicker,
      pickFile: pickFile,
    };
  }

  window.NetReconNewUICore = window.NetReconNewUICore || {};
  window.NetReconNewUICore.newUiRuntimes = window.NetReconNewUICore.newUiRuntimes || {};
  window.NetReconNewUICore.newUiRuntimes.createSessionSqliteRuntime = createSessionSqliteRuntime;
})();
