// Plugin parity between paired peers: presence + version of every installed // plugin. Applying state that references a plugin the // peer lacks (or runs at another version) can fail, so the admin is warned before // a sync. Plugin CONFIG is never compared or sent here, only names and versions. // This tenant's installed plugins ({ name, source, version }, by name). Schema- // qualified so a sub-tenant never reads the root tenant's plugin list. const localPluginList = async () => { const db = require("@saltcorn/data/db"); const schema = db.getTenantSchemaPrefix(); return (await db.query(`SELECT name, source, version FROM ${schema}_sc_plugins ORDER BY name`)).rows; }; // Human-readable warnings (empty when both lists match). const comparePluginLists = (localPlugins, peerPlugins) => { const localByName = new Map(localPlugins.map((pl) => [pl.name, pl])); const peerByName = new Map(peerPlugins.map((pl) => [pl.name, pl])); const warnings = []; for (const [name, mine] of localByName) { const theirs = peerByName.get(name); if (!theirs) { warnings.push(`peer missing plugin "${name}"`); } else if ((mine.version || "") !== (theirs.version || "")) { warnings.push(`plugin version mismatch on "${name}": local ${mine.version || "?"}, peer ${theirs.version || "?"}`); } } for (const name of peerByName.keys()) { if (!localByName.has(name)) { warnings.push(`peer has plugin not installed here: "${name}"`); } } return warnings; }; module.exports = { localPluginList, comparePluginLists };