@aglyn/plugins-marketplace 1.19.0 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/server/install-plugin.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { holdsStampedSiteKey } from '@aglyn/aglyn/app-utils/host-permissions'\nimport { MARKETPLACE_SITE_ROLE_PRESET, marketplaceSitePermissionRefusal } from '../model/marketplace-site-permissions'\nimport {\n resolveOrgIdForHost, firebaseAdmin } from '@aglyn/tenant-data-admin'\nimport {\n isCompatibleHostAbi,\n isFirstPartyPlugin,\n isPluginRevoked,\n newestInstallableVersion,\n PLUGIN_HOST_ABI_VERSION,\n type PluginApiHandler,\n type PluginRevocation,\n} from '@aglyn/aglyn/server'\nimport { dropPluginSiteCache } from '@aglyn/aglyn/plugin-manager/plugin-site-cache'\nimport { resolveOrgPermissions } from '@aglyn/tenant-runtime/org-permissions'\nimport { ownsListingHere } from './own-listing'\nimport { requirePurchase } from './purchase-entitlement'\nimport { pinnedProvenance } from './provenance'\nimport { recordVersionMove } from './version-stats'\nimport { isPublisherSecurityLocked } from './sale-risk'\nimport {\n isListingBrowsable,\n isPrivateListing,\n isVersionApproved,\n listingArtifactType,\n} from '../model/marketplace'\nimport { compareArtifactVersions } from '../model/update-state'\n\n/**\n * Installs (or upgrades) a marketplace plugin into a host (AGL-45), pinning a\n * version per AGL-43 §3.4. Writes `hosts/{hostId}/installs/{listingId}`\n * with the exact `{version, sha256}` — upgrades are explicit (a new pin),\n * so a publisher can never swap the code a consumer runs. Access mirrors\n * component installs: free, purchased (webhook-written), or own listing.\n * The pin references the immutable artifact; the sandboxed loader fetches\n * and integrity-checks it at render time.\n *\n * Enablement sync (AGL-424): marketplace plugins ride the same\n * `org.enabledPlugins` switchboard first-party plugins use — but ONLY for\n * workspaces that have explicitly configured the field. Install appends\n * the listing id there; uninstall removes it once no pin (org or acting\n * host) remains. Workspaces that never touched the switchboard stay\n * default-open (absent field ⇒ installs load), so pre-sync installs keep\n * working.\n */\n\n/** Keeps `org.enabledPlugins` in step with install pins (AGL-424). */\nasync function syncEnabledPlugins(\n firestore: FirebaseFirestore.Firestore,\n orgId: string | null,\n hostId: string,\n listingId: string,\n action: 'add' | 'remove-if-unpinned',\n): Promise<void> {\n if (!orgId) return\n // `enabledPlugins` is a flat mix of first-party bundle ids and marketplace\n // listing ids (AGL-777). A listing id is a Firestore doc id, so it can't\n // normally collide with a bundle name — but guard anyway: an install sync\n // must NEVER arrayUnion/arrayRemove a bundle id, which would silently\n // enable or disable a platform plugin as a side effect of a purchase.\n if (isFirstPartyPlugin(listingId)) return\n const orgRef = firestore.collection('orgs').doc(orgId)\n const configured = (await orgRef.get()).get('enabledPlugins')\n // Absent field = default-open workspace; nothing to sync.\n if (!Array.isArray(configured)) return\n if (action === 'add') {\n if (configured.includes(listingId)) return\n await orgRef.update({\n enabledPlugins: firebaseAdmin.firestore.FieldValue.arrayUnion(listingId),\n })\n return\n }\n const [orgPin, hostPin] = await Promise.all([\n orgRef.collection('installs').doc(listingId).get(),\n firestore\n .collection('hosts')\n .doc(hostId)\n .collection('installs')\n .doc(listingId)\n .get(),\n ])\n if (orgPin.exists || hostPin.exists) return\n await orgRef.update({\n enabledPlugins: firebaseAdmin.firestore.FieldValue.arrayRemove(listingId),\n })\n}\n/**\n * A pin that moved takes with it the declared connections it no longer\n * declares (AGL-4194): an uninstall, or a re-pin to a version without one.\n * Each such connection's grant is revoked at the provider (best effort, with\n * the endpoint the PREVIOUS pin declared, the new one having none) and the\n * connection and its record are deleted. A site that still runs the listing\n * through its other pin (its own, or the workspace's) keeps its connection.\n * Never throws: the pin itself is already written.\n */\nasync function forgetUndeclaredConnections(\n firestore: FirebaseFirestore.Firestore,\n options: {\n scope: 'org' | 'host'\n hostId: string\n orgId: string | null\n listingId: string\n previousPin: Record<string, unknown> | undefined\n },\n): Promise<void> {\n try {\n const { pinConnections, reconcileListingConnections } = await import('./connections/cleanup')\n const previous = pinConnections(options.previousPin)\n if (!previous.length) return\n const orgId = options.orgId ?? (await resolveOrgIdForHost(options.hostId)) ?? null\n const hostIds =\n options.scope === 'org' && orgId\n ? (await firestore.collection('hosts').where('orgId', '==', orgId).get()).docs.map((doc) => doc.id)\n : [options.hostId]\n await reconcileListingConnections(firestore, { hostIds, orgId, listingId: options.listingId, previous })\n } catch (error) {\n console.error('[marketplace] the connections of a moved pin were not cleaned up', error)\n }\n}\n\n/**\n * Tells the live sites a pin changed (AGL-3741, AGL-4121).\n *\n * The tenant caches each site's resolved plugins, and every page that read\n * them, under the site's plugin tag, and an install or uninstall reaches live\n * pages only through this drop — the cache behind it is held for the\n * platform's week-long backstop. An org-tier pin applies to every site in the\n * org, so it is those sites that are told.\n *\n * `plugins: 'host'` makes the tenant expire the plugin set immediately rather\n * than stale-while-revalidate, so an uninstalled bundle does not run for one\n * more visitor. The app stages each drop in its durable outbox first, so a\n * refused call is retried; the answer is `false` only when a drop neither\n * landed nor was queued, which the caller reports as a failure. Never throws:\n * the pin itself is already written.\n */\nasync function announcePinChange(\n firestore: FirebaseFirestore.Firestore,\n options: { scope: 'org' | 'host'; hostId: string; orgId: string | null; reason: string },\n): Promise<boolean> {\n try {\n const hostIds =\n options.scope === 'org' && options.orgId\n ? (\n await firestore.collection('hosts').where('orgId', '==', options.orgId).get()\n ).docs.map((doc) => doc.id)\n : [options.hostId]\n const result = await dropPluginSiteCache({\n hostIds,\n reason: options.reason,\n plugins: 'host',\n })\n return result.complete\n } catch (error) {\n console.error('[marketplace] the sites running a changed pin were not told', error)\n return false\n }\n}\n\n/** What an uninstall answers when its live-site drop was lost (AGL-4121). */\nconst PIN_DROP_LOST_ERROR =\n 'The plugin was removed, but the live site could not be refreshed. Try again to finish removing it from published pages.'\n\nexport const installPluginHandler: PluginApiHandler = async (req, res) => {\n if (req.method !== 'POST') {\n return res.status(405).json({ error: 'Method not allowed' })\n }\n const listingId = String(req.body?.listingId ?? '')\n const hostId = String(req.body?.hostId ?? '')\n // Optional explicit version for upgrade/downgrade; defaults to latest.\n const requestedVersion = req.body?.version\n ? String(req.body.version)\n : undefined\n if (!listingId || !hostId) {\n return res.status(400).json({ error: 'Missing listingId or hostId' })\n }\n const authorization = String(req.headers.authorization ?? '')\n const idToken = authorization.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length)\n : undefined\n if (!idToken) return res.status(401).json({ error: 'Unauthenticated' })\n\n try {\n const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken)\n const membership = await resolveOrgPermissions(decoded.uid, { hostId })\n if (!membership.permissions.installPlugins) {\n return res.status(403).json({\n error: 'Your organization role does not allow installing from the marketplace',\n })\n }\n const firestore = firebaseAdmin.app().firestore()\n\n const hostRef = firestore.collection('hosts').doc(hostId)\n const hostSnapshot = await hostRef.get()\n if (!hostSnapshot.exists) {\n return res.status(404).json({ error: 'Unknown site' })\n }\n // \"Install marketplace items into a site\" (AGL-4047): the site's stamp, else\n // the preset of the member's site role, the Admin and Editor this asked for.\n if (!holdsStampedSiteKey(hostSnapshot, decoded.uid, 'marketplace.siteInstall', MARKETPLACE_SITE_ROLE_PRESET)) {\n return res.status(403).json({ error: marketplaceSitePermissionRefusal('marketplace.siteInstall') })\n }\n\n // Uninstalls (AGL-237/424). Org pins are API-managed (rules deny\n // client writes); host uninstalls also come through here so the\n // enablement sync runs. Plugin-owned data is never deleted — only the\n // pin (and the switchboard entry when no pin remains anywhere) and, the\n // one exception, a declared connection's grant and record (AGL-4194).\n if (req.body?.action === 'uninstall') {\n const orgId = await resolveOrgIdForHost(hostId)\n if (req.body?.scope === 'org' && !orgId) {\n return res.status(409).json({ error: 'This site has no organization yet' })\n }\n const pinRef =\n req.body?.scope === 'org'\n ? orgId\n ? firestore.collection('orgs').doc(orgId).collection('installs').doc(listingId)\n : null\n : firestore.collection('hosts').doc(hostId).collection('installs').doc(listingId)\n // Active-install accounting (AGL-880): decrement only when a pin actually\n // existed, so a repeat/no-op uninstall never over-counts. `installCount`\n // is a cumulative all-time total and is deliberately left untouched.\n const pinSnapshot = pinRef ? await pinRef.get() : null\n const pinExisted = pinSnapshot?.exists ?? false\n // Read before the delete: the version being left is what the per-version\n // tally has to decrement, and it is gone the moment the pin is.\n const pinnedVersion = pinExisted ? pinSnapshot?.get('version') : null\n if (pinRef) await pinRef.delete()\n if (pinExisted) {\n await forgetUndeclaredConnections(firestore, {\n scope: req.body?.scope === 'org' ? 'org' : 'host',\n hostId,\n orgId,\n listingId,\n previousPin: pinSnapshot?.data(),\n })\n }\n // Announced even when no pin was found (AGL-4121): a retry after a lost\n // drop finds the pin already gone, and it is the retry that has to\n // reach the live pages. A repeat drop is a no-op.\n const told = await announcePinChange(firestore, {\n scope: req.body?.scope === 'org' ? 'org' : 'host',\n hostId,\n orgId,\n reason: `marketplace listing ${listingId} uninstalled`,\n })\n if (pinExisted) {\n const listingRef = firestore\n .collection('marketplaceListings')\n .doc(listingId)\n await recordVersionMove({\n firestore,\n listingRef,\n artifactType: 'plugin',\n from: pinnedVersion,\n to: null,\n })\n await firestore\n .runTransaction(async (tx) => {\n const snapshot = await tx.get(listingRef)\n const current = Number(snapshot.get('activeInstalls') ?? 0)\n tx.update(listingRef, { activeInstalls: Math.max(0, current - 1) })\n })\n .catch(() => undefined)\n }\n await syncEnabledPlugins(\n firestore,\n orgId,\n hostId,\n listingId,\n 'remove-if-unpinned',\n )\n if (!told) return res.status(503).json({ error: PIN_DROP_LOST_ERROR })\n return res.status(200).json({ ok: true })\n }\n\n const listingRef = firestore\n .collection('marketplaceListings')\n .doc(listingId)\n const listingSnapshot = await listingRef.get()\n const listing = listingSnapshot.data() as any\n if (\n !listing ||\n listing.deletedAt ||\n // Staff takedown blocks new installs too (AGL-948) — the revocation\n // check below stops a hidden listing anyway, but only while the\n // revocation stands, and a takedown is not conditional on it.\n listing.hiddenAt ||\n // A publisher under a SECURITY lock hands nothing over (AGL-3365).\n (await isPublisherSecurityLocked(firestore, listing.profileId)) ||\n listingArtifactType(listing) !== 'plugin'\n ) {\n return res.status(404).json({ error: 'Unknown plugin listing' })\n }\n\n // The publisher installs their own listing for free, and may install\n // an unapproved version of it to test. Org-owned now (AGL-652), so this\n // asks the publishing org's members (comparing a uid to an org id would\n // never match). Membership, not a permission (AGL-4047): it is a fact\n // about who published the listing.\n //\n // TWO facts, correlated (AGL-2484). Membership answers a question about\n // the PUBLISHER org alone; the install TARGET is a different org,\n // resolved from the host by the permission gate above. Nothing tied them\n // together, so one flag computed from org P waived three gates on an\n // install landing in org Q: review, version resolution, and payment. A\n // user who administers publisher org P and holds `installPlugins` on a\n // host in org Q could therefore push P's pending — or rejected — bundle\n // onto Q's live public site, for free.\n //\n // Same-org is a NECESSARY condition, never a sufficient one: the\n // membership check still has to pass, so this narrows the waiver and\n // cannot widen it. A listing with no `profileId` is owned by nobody and\n // waives nothing. Every install door asks this same definition now\n // (`ownsListingHere`, AGL-4047); plugins were the first to need it.\n const ownsListing = await ownsListingHere(firestore, {\n uid: decoded.uid,\n publisherOrgId: listing.profileId,\n targetOrgId: membership.orgId,\n })\n\n // Private plugins are installable ONLY by the owning org (AGL-968).\n // This is what makes private mean private; the UI merely hides them.\n if (isPrivateListing(listing) && !ownsListing) {\n return res.status(404).json({ error: 'Unknown plugin listing' })\n }\n\n // Installs enforce the same review state the marketplace browses by\n // (AGL-965). `isListingBrowsable` used to be applied ONLY by the browse\n // UI, so a listing that was never reviewed, was rejected, or has since\n // been de-listed could still be installed by anyone who knew its id and\n // posted here directly — which also made \"delist\" a suggestion rather\n // than a control. The publisher keeps installing their own listing to\n // test it before review.\n if (!isListingBrowsable(listing) && !isPrivateListing(listing) && !ownsListing) {\n return res\n .status(409)\n .json({ error: 'This plugin is not available for install' })\n }\n\n const priceUsd = Number(listing.priceUsd ?? 0)\n // A FULLY refunded purchase stops entitling (AGL-1546), and until\n // AGL-1699 only the component route knew that: this one asked whether a\n // purchase doc EXISTED, so buy/install/refund kept the artifact. The\n // predicate lives in one place now so the next route cannot miss it.\n const unpaid = await requirePurchase({\n firestore,\n buyerUid: decoded.uid,\n // THE ORG THE LICENCE HAS TO COVER (AGL-2331). `membership.orgId` is\n // resolved server-side from the caller's own membership by the\n // permission gate above — never a request-body field — so this is the\n // workspace the install actually lands in, and the only one a purchase\n // can entitle here.\n buyerOrgId: membership.orgId ?? '',\n listingId,\n priceUsd,\n ownsListing,\n })\n if (unpaid) return res.status(402).json(unpaid)\n\n // Installs resolve the newest APPROVED version, never `latestVersion`\n // (AGL-966). Before this, publishing v1.0.1 to a verified listing put\n // unreviewed code in front of every workspace immediately: the listing\n // kept its status, the queue never surfaced the update, and this line\n // pinned whatever had just been uploaded. A pending version is now\n // simply not offered, and the previously approved one keeps installing.\n //\n // The publisher may still install their own unapproved version — that\n // is how you test a plugin before submitting it. It reaches only their\n // own sites because `ownsListing` now REQUIRES the install to land in\n // the publishing org (AGL-2484); this sentence used to be an assertion\n // about code that did not check.\n // Read the kill switch BEFORE resolving, not after (AGL-2368).\n //\n // The order used to be: pick the newest APPROVED version, then 409 if that\n // one happens to be revoked. Revocation was never an input to the choice,\n // only a veto on it — so stopping one build stopped the whole listing.\n // v2.0.0 revoked with v1.0.0 approved and installable resolved to v2.0.0\n // and answered 409, and `requirePurchase` above has already taken the\n // buyer's money by then. A per-version revocation is deliberately NOT\n // listing-wide (`isListingWideRevocation`); that path made it so anyway.\n const revocation = (\n await firestore.collection('revocations').doc(listingId).get()\n ).data() as PluginRevocation | undefined\n\n let version = requestedVersion ?? ''\n let versionData: any\n if (version) {\n versionData = (\n await listingRef.collection('pluginVersions').doc(version).get()\n ).data()\n if (!isVersionApproved(versionData) && !ownsListing) {\n return res.status(409).json({\n error: 'That version has not passed review yet',\n reviewState: versionData?.reviewState ?? 'unknown',\n })\n }\n } else {\n const approvedSnapshot = await listingRef\n .collection('pluginVersions')\n .orderBy('publishedAt', 'desc')\n .limit(25)\n .get()\n const candidates = approvedSnapshot.docs.map((doc) => ({\n version: String(doc.get('version') ?? doc.id),\n reviewState: doc.get('reviewState'),\n publishedAt: doc.get('publishedAt'),\n data: doc.data(),\n }))\n // The SHARED predicate (AGL-2306/2368): approved AND not revoked, which\n // is the same function `repairLatestApprovedVersion` writes the listing\n // mirror from. Two readings of \"what is on offer\" is how the badge and\n // the route came to disagree; one function is how they cannot.\n //\n // Note this also orders by version rather than by `publishedAt`, which\n // is what the mirror has always done — so the version this route hands\n // over is now the version the listing page advertised.\n const newestVersion = newestInstallableVersion(\n candidates,\n revocation,\n compareArtifactVersions,\n )\n const newest = newestVersion\n ? (candidates.find((entry) => entry.version === newestVersion) as\n | { version: string; data: any }\n | undefined)\n : undefined\n // The publisher testing their own listing falls back to latest.\n const fallback = ownsListing\n ? candidates.find(\n (entry) => entry.version === String(listing.latestVersion ?? ''),\n )\n : undefined\n const chosen = newest ?? fallback\n if (!chosen) {\n return res.status(409).json({\n error: 'This plugin has no reviewed version available to install',\n })\n }\n version = chosen.version\n versionData = (chosen as { data?: unknown }).data\n }\n if (!versionData?.sha256 || !versionData?.manifest) {\n return res.status(404).json({ error: 'Unknown plugin version' })\n }\n\n // Kill switch (AGL-43 §3.5): a revoked version can't be installed.\n // Shares the predicate with the loaders (AGL-1085) so \"installable\" and\n // \"runnable\" cannot drift apart.\n //\n // Still checked after resolution, and still load-bearing: the unpinned\n // branch can no longer choose a revoked version, but an EXPLICIT pin and\n // the publisher's `latestVersion` fallback both can, and a listing-wide\n // takedown revokes whatever either of them names.\n if (isPluginRevoked(revocation, version)) {\n return res\n .status(409)\n .json({ error: 'This plugin version has been revoked' })\n }\n\n const now = firebaseAdmin.firestore.FieldValue.serverTimestamp()\n // Install tier (AGL-237): org-extending plugins install once for every\n // host in the org; host-only plugins stay pinned to the host.\n const scope = req.body?.scope === 'org' ? 'org' : 'host'\n let installRef = hostRef.collection('installs').doc(listingId)\n let pinOrgId: string | null = null\n if (scope === 'org') {\n const orgId = await resolveOrgIdForHost(hostId)\n pinOrgId = orgId ?? null\n if (!orgId) {\n return res.status(409).json({ error: 'This site has no organization yet' })\n }\n installRef = firestore\n .collection('orgs')\n .doc(orgId)\n .collection('installs')\n .doc(listingId)\n }\n const existing = await installRef.get()\n const pin = {\n listingId,\n profileId: listing.profileId,\n displayName: listing.displayName,\n pluginId: versionData.manifest.id,\n version,\n sha256: versionData.sha256,\n objectPath: versionData.objectPath,\n manifest: versionData.manifest,\n // The same provenance stamp every other artifact type now carries\n // (AGL-1015), so one reader answers \"where did this come from\" for\n // pinned and copied artifacts alike. The flat pin fields stay: they\n // are what the loader reads.\n installedFrom: pinnedProvenance({\n listingId,\n listing,\n version,\n sha256: versionData.sha256,\n artifactType: 'plugin',\n }),\n ...(existing.exists ? {} : { installedAt: now }),\n updatedAt: now,\n }\n // Each field replaced WHOLE (AGL-4194). `merge: true` merged the new\n // version's manifest INTO the old one's, so a key the new version dropped\n // — its `connections`, say — survived on the pin and went on being read\n // as declared. The fields not written here are still kept.\n await installRef.set(pin, { mergeFields: Object.keys(pin) })\n if (existing.exists) {\n await forgetUndeclaredConnections(firestore, {\n scope,\n hostId,\n orgId: pinOrgId,\n listingId,\n previousPin: existing.data(),\n })\n }\n await announcePinChange(firestore, {\n scope,\n hostId,\n orgId: pinOrgId,\n reason: `marketplace listing ${listingId} installed at ${version}`,\n })\n if (!existing.exists) {\n await listingRef\n .update({\n // installCount is the cumulative all-time total; activeInstalls\n // mirrors live pins and is decremented on uninstall (AGL-880).\n installCount: firebaseAdmin.firestore.FieldValue.increment(1),\n activeInstalls: firebaseAdmin.firestore.FieldValue.increment(1),\n })\n .catch(() => undefined)\n }\n // Per-version tally (AGL-1036). This runs on EVERY pin write, not just the\n // first: a re-pin is the common case and the one the listing-level counters\n // above are blind to — the pin already exists, so nothing there moves while\n // the install changes version underneath.\n await recordVersionMove({\n firestore,\n listingRef,\n artifactType: 'plugin',\n from: existing.exists ? existing.get('version') : null,\n to: version,\n })\n\n // Enablement sync (AGL-424): the new install loads on next visit for\n // workspaces with a configured switchboard.\n await syncEnabledPlugins(\n firestore,\n await resolveOrgIdForHost(hostId),\n hostId,\n listingId,\n 'add',\n )\n\n // ABI heads-up (AGL-429): the pin succeeds (explicit choice), but the\n // loaders will refuse an incompatible bundle — tell the installer now.\n const manifestHostAbi = Number(versionData.manifest?.hostAbi)\n const hostAbiWarning =\n Number.isInteger(manifestHostAbi) &&\n manifestHostAbi > 0 &&\n !isCompatibleHostAbi(manifestHostAbi)\n ? `Built for host ABI ${manifestHostAbi}; this platform runs ` +\n `${PLUGIN_HOST_ABI_VERSION}. The plugin will not load until the ` +\n 'publisher ships a compatible version.'\n : undefined\n\n return res.status(200).json({\n installed: true,\n upgraded: existing.exists,\n version,\n ...(hostAbiWarning ? { hostAbiWarning } : {}),\n })\n } catch (error) {\n console.error(error)\n return res.status(500).json({ error: 'Install failed' })\n }\n}\n"],"names":["holdsStampedSiteKey","MARKETPLACE_SITE_ROLE_PRESET","marketplaceSitePermissionRefusal","resolveOrgIdForHost","firebaseAdmin","isCompatibleHostAbi","isFirstPartyPlugin","isPluginRevoked","newestInstallableVersion","PLUGIN_HOST_ABI_VERSION","dropPluginSiteCache","resolveOrgPermissions","ownsListingHere","requirePurchase","pinnedProvenance","recordVersionMove","isPublisherSecurityLocked","isListingBrowsable","isPrivateListing","isVersionApproved","listingArtifactType","compareArtifactVersions","syncEnabledPlugins","firestore","orgId","hostId","listingId","action","orgRef","collection","doc","configured","get","Array","isArray","includes","update","enabledPlugins","FieldValue","arrayUnion","orgPin","hostPin","Promise","all","exists","arrayRemove","forgetUndeclaredConnections","options","pinConnections","reconcileListingConnections","previous","previousPin","length","hostIds","scope","where","docs","map","id","error","console","announcePinChange","result","reason","plugins","complete","PIN_DROP_LOST_ERROR","installPluginHandler","req","res","method","status","json","String","body","requestedVersion","version","undefined","authorization","headers","idToken","startsWith","slice","listing","membership","versionData","decoded","app","auth","verifyIdToken","uid","permissions","installPlugins","hostRef","hostSnapshot","pinRef","pinSnapshot","pinExisted","pinnedVersion","delete","data","told","listingRef","artifactType","from","to","runTransaction","tx","snapshot","current","Number","activeInstalls","Math","max","catch","ok","listingSnapshot","deletedAt","hiddenAt","profileId","ownsListing","publisherOrgId","targetOrgId","priceUsd","unpaid","buyerUid","buyerOrgId","revocation","reviewState","approvedSnapshot","orderBy","limit","candidates","publishedAt","newestVersion","newest","find","entry","fallback","latestVersion","chosen","sha256","manifest","now","serverTimestamp","installRef","pinOrgId","existing","pin","displayName","pluginId","objectPath","installedFrom","installedAt","updatedAt","set","mergeFields","Object","keys","installCount","increment","manifestHostAbi","hostAbi","hostAbiWarning","isInteger","installed","upgraded"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/server/install-plugin.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { holdsStampedSiteKey } from '@aglyn/aglyn/app-utils/host-permissions'\nimport { MARKETPLACE_SITE_ROLE_PRESET, marketplaceSitePermissionRefusal } from '../model/marketplace-site-permissions'\nimport {\n memberHasPermissionOnHost,\n resolveOrgIdForHost,\n resolveOrgMembership,\n firebaseAdmin,\n} from '@aglyn/tenant-data-admin'\nimport { permissionRefusalText } from '@aglyn/aglyn/app-utils/org-permissions'\nimport {\n pluginRunsOnChoiceFrom,\n writePluginRunsOn,\n} from '@aglyn/tenant-data-admin/server/plugin-runs-on'\nimport {\n isCompatibleHostAbi,\n isFirstPartyPlugin,\n isPluginRevoked,\n newestInstallableVersion,\n PLUGIN_HOST_ABI_VERSION,\n type PluginApiHandler,\n type PluginRevocation,\n} from '@aglyn/aglyn/server'\nimport { dropPluginSiteCache } from '@aglyn/aglyn/plugin-manager/plugin-site-cache'\nimport { resolveOrgPermissions } from '@aglyn/tenant-runtime/org-permissions'\nimport { ownsListingHere } from './own-listing'\nimport { requirePurchase } from './purchase-entitlement'\nimport { pinnedProvenance } from './provenance'\nimport { recordVersionMove } from './version-stats'\nimport { isPublisherSecurityLocked } from './sale-risk'\nimport {\n isListingBrowsable,\n isPrivateListing,\n isVersionApproved,\n listingArtifactType,\n} from '../model/marketplace'\nimport { compareArtifactVersions } from '../model/update-state'\n\n/**\n * Installs (or upgrades) a marketplace plugin into a host (AGL-45), pinning a\n * version per AGL-43 §3.4. Writes `hosts/{hostId}/installs/{listingId}`\n * with the exact `{version, sha256}` — upgrades are explicit (a new pin),\n * so a publisher can never swap the code a consumer runs. Access mirrors\n * component installs: free, purchased (webhook-written), or own listing.\n * The pin references the immutable artifact; the sandboxed loader fetches\n * and integrity-checks it at render time.\n *\n * Enablement sync (AGL-424): marketplace plugins ride the same\n * `org.enabledPlugins` switchboard first-party plugins use — but ONLY for\n * workspaces that have explicitly configured the field. Install appends\n * the listing id there; uninstall removes it once no pin (org or acting\n * host) remains. Workspaces that never touched the switchboard stay\n * default-open (absent field ⇒ installs load), so pre-sync installs keep\n * working.\n */\n\n/** Keeps `org.enabledPlugins` in step with install pins (AGL-424). */\nasync function syncEnabledPlugins(\n firestore: FirebaseFirestore.Firestore,\n orgId: string | null,\n hostId: string,\n listingId: string,\n action: 'add' | 'remove-if-unpinned',\n): Promise<void> {\n if (!orgId) return\n // `enabledPlugins` is a flat mix of first-party bundle ids and marketplace\n // listing ids (AGL-777). A listing id is a Firestore doc id, so it can't\n // normally collide with a bundle name — but guard anyway: an install sync\n // must NEVER arrayUnion/arrayRemove a bundle id, which would silently\n // enable or disable a platform plugin as a side effect of a purchase.\n if (isFirstPartyPlugin(listingId)) return\n const orgRef = firestore.collection('orgs').doc(orgId)\n const configured = (await orgRef.get()).get('enabledPlugins')\n // Absent field = default-open workspace; nothing to sync.\n if (!Array.isArray(configured)) return\n if (action === 'add') {\n if (configured.includes(listingId)) return\n await orgRef.update({\n enabledPlugins: firebaseAdmin.firestore.FieldValue.arrayUnion(listingId),\n })\n return\n }\n const [orgPin, hostPin] = await Promise.all([\n orgRef.collection('installs').doc(listingId).get(),\n firestore\n .collection('hosts')\n .doc(hostId)\n .collection('installs')\n .doc(listingId)\n .get(),\n ])\n if (orgPin.exists || hostPin.exists) return\n await orgRef.update({\n enabledPlugins: firebaseAdmin.firestore.FieldValue.arrayRemove(listingId),\n })\n}\n/**\n * A pin that moved takes with it the declared connections it no longer\n * declares (AGL-4194): an uninstall, or a re-pin to a version without one.\n * Each such connection's grant is revoked at the provider (best effort, with\n * the endpoint the PREVIOUS pin declared, the new one having none) and the\n * connection and its record are deleted. A site that still runs the listing\n * through its other pin (its own, or the workspace's) keeps its connection.\n * Never throws: the pin itself is already written.\n */\nasync function forgetUndeclaredConnections(\n firestore: FirebaseFirestore.Firestore,\n options: {\n scope: 'org' | 'host'\n hostId: string\n orgId: string | null\n listingId: string\n previousPin: Record<string, unknown> | undefined\n },\n): Promise<void> {\n try {\n const { pinConnections, reconcileListingConnections } = await import('./connections/cleanup')\n const previous = pinConnections(options.previousPin)\n if (!previous.length) return\n const orgId = options.orgId ?? (await resolveOrgIdForHost(options.hostId)) ?? null\n const hostIds =\n options.scope === 'org' && orgId\n ? (await firestore.collection('hosts').where('orgId', '==', orgId).get()).docs.map((doc) => doc.id)\n : [options.hostId]\n await reconcileListingConnections(firestore, { hostIds, orgId, listingId: options.listingId, previous })\n } catch (error) {\n console.error('[marketplace] the connections of a moved pin were not cleaned up', error)\n }\n}\n\n/**\n * Tells the live sites a pin changed (AGL-3741, AGL-4121).\n *\n * The tenant caches each site's resolved plugins, and every page that read\n * them, under the site's plugin tag, and an install or uninstall reaches live\n * pages only through this drop — the cache behind it is held for the\n * platform's week-long backstop. An org-tier pin applies to every site in the\n * org, so it is those sites that are told.\n *\n * `plugins: 'host'` makes the tenant expire the plugin set immediately rather\n * than stale-while-revalidate, so an uninstalled bundle does not run for one\n * more visitor. The app stages each drop in its durable outbox first, so a\n * refused call is retried; the answer is `false` only when a drop neither\n * landed nor was queued, which the caller reports as a failure. Never throws:\n * the pin itself is already written.\n */\nasync function announcePinChange(\n firestore: FirebaseFirestore.Firestore,\n options: { scope: 'org' | 'host'; hostId: string; orgId: string | null; reason: string },\n): Promise<boolean> {\n try {\n const hostIds =\n options.scope === 'org' && options.orgId\n ? (\n await firestore.collection('hosts').where('orgId', '==', options.orgId).get()\n ).docs.map((doc) => doc.id)\n : [options.hostId]\n const result = await dropPluginSiteCache({\n hostIds,\n reason: options.reason,\n plugins: 'host',\n })\n return result.complete\n } catch (error) {\n console.error('[marketplace] the sites running a changed pin were not told', error)\n return false\n }\n}\n\n/** What an uninstall answers when its live-site drop was lost (AGL-4121). */\nconst PIN_DROP_LOST_ERROR =\n 'The plugin was removed, but the live site could not be refreshed. Try again to finish removing it from published pages.'\n\nexport const installPluginHandler: PluginApiHandler = async (req, res) => {\n if (req.method !== 'POST') {\n return res.status(405).json({ error: 'Method not allowed' })\n }\n const listingId = String(req.body?.listingId ?? '')\n const hostId = String(req.body?.hostId ?? '')\n // Optional explicit version for upgrade/downgrade; defaults to latest.\n const requestedVersion = req.body?.version\n ? String(req.body.version)\n : undefined\n if (!listingId || !hostId) {\n return res.status(400).json({ error: 'Missing listingId or hostId' })\n }\n const authorization = String(req.headers.authorization ?? '')\n const idToken = authorization.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length)\n : undefined\n if (!idToken) return res.status(401).json({ error: 'Unauthenticated' })\n\n try {\n const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken)\n const membership = await resolveOrgPermissions(decoded.uid, { hostId })\n if (!membership.permissions.installPlugins) {\n return res.status(403).json({\n error: 'Your organization role does not allow installing from the marketplace',\n })\n }\n const firestore = firebaseAdmin.app().firestore()\n\n const hostRef = firestore.collection('hosts').doc(hostId)\n const hostSnapshot = await hostRef.get()\n if (!hostSnapshot.exists) {\n return res.status(404).json({ error: 'Unknown site' })\n }\n // \"Install marketplace items into a site\" (AGL-4047): the site's stamp, else\n // the preset of the member's site role, the Admin and Editor this asked for.\n if (!holdsStampedSiteKey(hostSnapshot, decoded.uid, 'marketplace.siteInstall', MARKETPLACE_SITE_ROLE_PRESET)) {\n return res.status(403).json({ error: marketplaceSitePermissionRefusal('marketplace.siteInstall') })\n }\n\n // Uninstalls (AGL-237/424). Org pins are API-managed (rules deny\n // client writes); host uninstalls also come through here so the\n // enablement sync runs. Plugin-owned data is never deleted — only the\n // pin (and the switchboard entry when no pin remains anywhere) and, the\n // one exception, a declared connection's grant and record (AGL-4194).\n if (req.body?.action === 'uninstall') {\n const orgId = await resolveOrgIdForHost(hostId)\n if (req.body?.scope === 'org' && !orgId) {\n return res.status(409).json({ error: 'This site has no organization yet' })\n }\n const pinRef =\n req.body?.scope === 'org'\n ? orgId\n ? firestore.collection('orgs').doc(orgId).collection('installs').doc(listingId)\n : null\n : firestore.collection('hosts').doc(hostId).collection('installs').doc(listingId)\n // Active-install accounting (AGL-880): decrement only when a pin actually\n // existed, so a repeat/no-op uninstall never over-counts. `installCount`\n // is a cumulative all-time total and is deliberately left untouched.\n const pinSnapshot = pinRef ? await pinRef.get() : null\n const pinExisted = pinSnapshot?.exists ?? false\n // Read before the delete: the version being left is what the per-version\n // tally has to decrement, and it is gone the moment the pin is.\n const pinnedVersion = pinExisted ? pinSnapshot?.get('version') : null\n if (pinRef) await pinRef.delete()\n if (pinExisted) {\n await forgetUndeclaredConnections(firestore, {\n scope: req.body?.scope === 'org' ? 'org' : 'host',\n hostId,\n orgId,\n listingId,\n previousPin: pinSnapshot?.data(),\n })\n }\n // Announced even when no pin was found (AGL-4121): a retry after a lost\n // drop finds the pin already gone, and it is the retry that has to\n // reach the live pages. A repeat drop is a no-op.\n const told = await announcePinChange(firestore, {\n scope: req.body?.scope === 'org' ? 'org' : 'host',\n hostId,\n orgId,\n reason: `marketplace listing ${listingId} uninstalled`,\n })\n if (pinExisted) {\n const listingRef = firestore\n .collection('marketplaceListings')\n .doc(listingId)\n await recordVersionMove({\n firestore,\n listingRef,\n artifactType: 'plugin',\n from: pinnedVersion,\n to: null,\n })\n await firestore\n .runTransaction(async (tx) => {\n const snapshot = await tx.get(listingRef)\n const current = Number(snapshot.get('activeInstalls') ?? 0)\n tx.update(listingRef, { activeInstalls: Math.max(0, current - 1) })\n })\n .catch(() => undefined)\n }\n await syncEnabledPlugins(\n firestore,\n orgId,\n hostId,\n listingId,\n 'remove-if-unpinned',\n )\n if (!told) return res.status(503).json({ error: PIN_DROP_LOST_ERROR })\n return res.status(200).json({ ok: true })\n }\n\n const listingRef = firestore\n .collection('marketplaceListings')\n .doc(listingId)\n const listingSnapshot = await listingRef.get()\n const listing = listingSnapshot.data() as any\n if (\n !listing ||\n listing.deletedAt ||\n // Staff takedown blocks new installs too (AGL-948) — the revocation\n // check below stops a hidden listing anyway, but only while the\n // revocation stands, and a takedown is not conditional on it.\n listing.hiddenAt ||\n // A publisher under a SECURITY lock hands nothing over (AGL-3365).\n (await isPublisherSecurityLocked(firestore, listing.profileId)) ||\n listingArtifactType(listing) !== 'plugin'\n ) {\n return res.status(404).json({ error: 'Unknown plugin listing' })\n }\n\n // The publisher installs their own listing for free, and may install\n // an unapproved version of it to test. Org-owned now (AGL-652), so this\n // asks the publishing org's members (comparing a uid to an org id would\n // never match). Membership, not a permission (AGL-4047): it is a fact\n // about who published the listing.\n //\n // TWO facts, correlated (AGL-2484). Membership answers a question about\n // the PUBLISHER org alone; the install TARGET is a different org,\n // resolved from the host by the permission gate above. Nothing tied them\n // together, so one flag computed from org P waived three gates on an\n // install landing in org Q: review, version resolution, and payment. A\n // user who administers publisher org P and holds `installPlugins` on a\n // host in org Q could therefore push P's pending — or rejected — bundle\n // onto Q's live public site, for free.\n //\n // Same-org is a NECESSARY condition, never a sufficient one: the\n // membership check still has to pass, so this narrows the waiver and\n // cannot widen it. A listing with no `profileId` is owned by nobody and\n // waives nothing. Every install door asks this same definition now\n // (`ownsListingHere`, AGL-4047); plugins were the first to need it.\n const ownsListing = await ownsListingHere(firestore, {\n uid: decoded.uid,\n publisherOrgId: listing.profileId,\n targetOrgId: membership.orgId,\n })\n\n // Private plugins are installable ONLY by the owning org (AGL-968).\n // This is what makes private mean private; the UI merely hides them.\n if (isPrivateListing(listing) && !ownsListing) {\n return res.status(404).json({ error: 'Unknown plugin listing' })\n }\n\n // Installs enforce the same review state the marketplace browses by\n // (AGL-965). `isListingBrowsable` used to be applied ONLY by the browse\n // UI, so a listing that was never reviewed, was rejected, or has since\n // been de-listed could still be installed by anyone who knew its id and\n // posted here directly — which also made \"delist\" a suggestion rather\n // than a control. The publisher keeps installing their own listing to\n // test it before review.\n if (!isListingBrowsable(listing) && !isPrivateListing(listing) && !ownsListing) {\n return res\n .status(409)\n .json({ error: 'This plugin is not available for install' })\n }\n\n const priceUsd = Number(listing.priceUsd ?? 0)\n // A FULLY refunded purchase stops entitling (AGL-1546), and until\n // AGL-1699 only the component route knew that: this one asked whether a\n // purchase doc EXISTED, so buy/install/refund kept the artifact. The\n // predicate lives in one place now so the next route cannot miss it.\n const unpaid = await requirePurchase({\n firestore,\n buyerUid: decoded.uid,\n // THE ORG THE LICENCE HAS TO COVER (AGL-2331). `membership.orgId` is\n // resolved server-side from the caller's own membership by the\n // permission gate above — never a request-body field — so this is the\n // workspace the install actually lands in, and the only one a purchase\n // can entitle here.\n buyerOrgId: membership.orgId ?? '',\n listingId,\n priceUsd,\n ownsListing,\n })\n if (unpaid) return res.status(402).json(unpaid)\n\n // Installs resolve the newest APPROVED version, never `latestVersion`\n // (AGL-966). Before this, publishing v1.0.1 to a verified listing put\n // unreviewed code in front of every workspace immediately: the listing\n // kept its status, the queue never surfaced the update, and this line\n // pinned whatever had just been uploaded. A pending version is now\n // simply not offered, and the previously approved one keeps installing.\n //\n // The publisher may still install their own unapproved version — that\n // is how you test a plugin before submitting it. It reaches only their\n // own sites because `ownsListing` now REQUIRES the install to land in\n // the publishing org (AGL-2484); this sentence used to be an assertion\n // about code that did not check.\n // Read the kill switch BEFORE resolving, not after (AGL-2368).\n //\n // The order used to be: pick the newest APPROVED version, then 409 if that\n // one happens to be revoked. Revocation was never an input to the choice,\n // only a veto on it — so stopping one build stopped the whole listing.\n // v2.0.0 revoked with v1.0.0 approved and installable resolved to v2.0.0\n // and answered 409, and `requirePurchase` above has already taken the\n // buyer's money by then. A per-version revocation is deliberately NOT\n // listing-wide (`isListingWideRevocation`); that path made it so anyway.\n const revocation = (\n await firestore.collection('revocations').doc(listingId).get()\n ).data() as PluginRevocation | undefined\n\n let version = requestedVersion ?? ''\n let versionData: any\n if (version) {\n versionData = (\n await listingRef.collection('pluginVersions').doc(version).get()\n ).data()\n if (!isVersionApproved(versionData) && !ownsListing) {\n return res.status(409).json({\n error: 'That version has not passed review yet',\n reviewState: versionData?.reviewState ?? 'unknown',\n })\n }\n } else {\n const approvedSnapshot = await listingRef\n .collection('pluginVersions')\n .orderBy('publishedAt', 'desc')\n .limit(25)\n .get()\n const candidates = approvedSnapshot.docs.map((doc) => ({\n version: String(doc.get('version') ?? doc.id),\n reviewState: doc.get('reviewState'),\n publishedAt: doc.get('publishedAt'),\n data: doc.data(),\n }))\n // The SHARED predicate (AGL-2306/2368): approved AND not revoked, which\n // is the same function `repairLatestApprovedVersion` writes the listing\n // mirror from. Two readings of \"what is on offer\" is how the badge and\n // the route came to disagree; one function is how they cannot.\n //\n // Note this also orders by version rather than by `publishedAt`, which\n // is what the mirror has always done — so the version this route hands\n // over is now the version the listing page advertised.\n const newestVersion = newestInstallableVersion(\n candidates,\n revocation,\n compareArtifactVersions,\n )\n const newest = newestVersion\n ? (candidates.find((entry) => entry.version === newestVersion) as\n | { version: string; data: any }\n | undefined)\n : undefined\n // The publisher testing their own listing falls back to latest.\n const fallback = ownsListing\n ? candidates.find(\n (entry) => entry.version === String(listing.latestVersion ?? ''),\n )\n : undefined\n const chosen = newest ?? fallback\n if (!chosen) {\n return res.status(409).json({\n error: 'This plugin has no reviewed version available to install',\n })\n }\n version = chosen.version\n versionData = (chosen as { data?: unknown }).data\n }\n if (!versionData?.sha256 || !versionData?.manifest) {\n return res.status(404).json({ error: 'Unknown plugin version' })\n }\n\n // Kill switch (AGL-43 §3.5): a revoked version can't be installed.\n // Shares the predicate with the loaders (AGL-1085) so \"installable\" and\n // \"runnable\" cannot drift apart.\n //\n // Still checked after resolution, and still load-bearing: the unpinned\n // branch can no longer choose a revoked version, but an EXPLICIT pin and\n // the publisher's `latestVersion` fallback both can, and a listing-wide\n // takedown revokes whatever either of them names.\n if (isPluginRevoked(revocation, version)) {\n return res\n .status(409)\n .json({ error: 'This plugin version has been revoked' })\n }\n\n const now = firebaseAdmin.firestore.FieldValue.serverTimestamp()\n // Install tier (AGL-237): org-extending plugins install once for every\n // host in the org; host-only plugins stay pinned to the host.\n const scope = req.body?.scope === 'org' ? 'org' : 'host'\n let installRef = hostRef.collection('installs').doc(listingId)\n let pinOrgId: string | null = null\n if (scope === 'org') {\n const orgId = await resolveOrgIdForHost(hostId)\n pinOrgId = orgId ?? null\n if (!orgId) {\n return res.status(409).json({ error: 'This site has no organization yet' })\n }\n installRef = firestore\n .collection('orgs')\n .doc(orgId)\n .collection('installs')\n .doc(listingId)\n }\n // \"All sites except…\" (AGL-4047): a workspace install that leaves some\n // sites out. Each site left out gets the same entry its own Admin › Plugins\n // switch writes, and the realm join drops a listing a site switched off,\n // so the plugin runs everywhere else, a site added later included, and a\n // site left out can turn it back on for itself. Written BEFORE the pin, and\n // only once every check above has passed, so a refused install leaves no\n // entry behind and a site left out never runs it, not even for one page.\n // Leaving a site out takes \"Manage site settings\" there, as its own switch\n // does; one site that refuses writes nothing at all.\n let leftOutHostIds: string[] = []\n if (req.body?.runsOn != null) {\n const choice = pluginRunsOnChoiceFrom(req.body.runsOn)\n if (\n choice?.mode !== 'allExcept' ||\n scope !== 'org' ||\n !pinOrgId ||\n isFirstPartyPlugin(listingId)\n ) {\n return res.status(400).json({\n error: 'Only an install for every site can leave sites out.',\n })\n }\n const orgId = pinOrgId\n const member = (await resolveOrgMembership(decoded.uid, orgId))?.member\n const written = await writePluginRunsOn({\n firestore,\n orgId,\n pluginId: listingId,\n choice,\n installing: true,\n label: String(listing.displayName ?? '').trim() || undefined,\n mayChangeSite: (siteId) =>\n memberHasPermissionOnHost(orgId, siteId, member, 'site.manage'),\n refusedOnSite: (siteName) =>\n `On ${siteName}: ${permissionRefusalText('site.manage')}`,\n now: () => now,\n })\n // `in`, not `!written.ok`: without strict null checks a boolean tag doesn't narrow.\n if ('problem' in written) {\n return res.status(written.status).json({ error: written.problem })\n }\n leftOutHostIds = written.leftOutHostIds\n }\n const existing = await installRef.get()\n const pin = {\n listingId,\n profileId: listing.profileId,\n displayName: listing.displayName,\n pluginId: versionData.manifest.id,\n version,\n sha256: versionData.sha256,\n objectPath: versionData.objectPath,\n manifest: versionData.manifest,\n // The same provenance stamp every other artifact type now carries\n // (AGL-1015), so one reader answers \"where did this come from\" for\n // pinned and copied artifacts alike. The flat pin fields stay: they\n // are what the loader reads.\n installedFrom: pinnedProvenance({\n listingId,\n listing,\n version,\n sha256: versionData.sha256,\n artifactType: 'plugin',\n }),\n ...(existing.exists ? {} : { installedAt: now }),\n updatedAt: now,\n }\n // Each field replaced WHOLE (AGL-4194). `merge: true` merged the new\n // version's manifest INTO the old one's, so a key the new version dropped\n // — its `connections`, say — survived on the pin and went on being read\n // as declared. The fields not written here are still kept.\n await installRef.set(pin, { mergeFields: Object.keys(pin) })\n if (existing.exists) {\n await forgetUndeclaredConnections(firestore, {\n scope,\n hostId,\n orgId: pinOrgId,\n listingId,\n previousPin: existing.data(),\n })\n }\n await announcePinChange(firestore, {\n scope,\n hostId,\n orgId: pinOrgId,\n reason: `marketplace listing ${listingId} installed at ${version}`,\n })\n if (!existing.exists) {\n await listingRef\n .update({\n // installCount is the cumulative all-time total; activeInstalls\n // mirrors live pins and is decremented on uninstall (AGL-880).\n installCount: firebaseAdmin.firestore.FieldValue.increment(1),\n activeInstalls: firebaseAdmin.firestore.FieldValue.increment(1),\n })\n .catch(() => undefined)\n }\n // Per-version tally (AGL-1036). This runs on EVERY pin write, not just the\n // first: a re-pin is the common case and the one the listing-level counters\n // above are blind to — the pin already exists, so nothing there moves while\n // the install changes version underneath.\n await recordVersionMove({\n firestore,\n listingRef,\n artifactType: 'plugin',\n from: existing.exists ? existing.get('version') : null,\n to: version,\n })\n\n // Enablement sync (AGL-424): the new install loads on next visit for\n // workspaces with a configured switchboard.\n await syncEnabledPlugins(\n firestore,\n await resolveOrgIdForHost(hostId),\n hostId,\n listingId,\n 'add',\n )\n\n // ABI heads-up (AGL-429): the pin succeeds (explicit choice), but the\n // loaders will refuse an incompatible bundle — tell the installer now.\n const manifestHostAbi = Number(versionData.manifest?.hostAbi)\n const hostAbiWarning =\n Number.isInteger(manifestHostAbi) &&\n manifestHostAbi > 0 &&\n !isCompatibleHostAbi(manifestHostAbi)\n ? `Built for host ABI ${manifestHostAbi}; this platform runs ` +\n `${PLUGIN_HOST_ABI_VERSION}. The plugin will not load until the ` +\n 'publisher ships a compatible version.'\n : undefined\n\n return res.status(200).json({\n installed: true,\n upgraded: existing.exists,\n version,\n ...(hostAbiWarning ? { hostAbiWarning } : {}),\n ...(leftOutHostIds.length ? { leftOutHostIds } : {}),\n })\n } catch (error) {\n console.error(error)\n return res.status(500).json({ error: 'Install failed' })\n }\n}\n"],"names":["holdsStampedSiteKey","MARKETPLACE_SITE_ROLE_PRESET","marketplaceSitePermissionRefusal","memberHasPermissionOnHost","resolveOrgIdForHost","resolveOrgMembership","firebaseAdmin","permissionRefusalText","pluginRunsOnChoiceFrom","writePluginRunsOn","isCompatibleHostAbi","isFirstPartyPlugin","isPluginRevoked","newestInstallableVersion","PLUGIN_HOST_ABI_VERSION","dropPluginSiteCache","resolveOrgPermissions","ownsListingHere","requirePurchase","pinnedProvenance","recordVersionMove","isPublisherSecurityLocked","isListingBrowsable","isPrivateListing","isVersionApproved","listingArtifactType","compareArtifactVersions","syncEnabledPlugins","firestore","orgId","hostId","listingId","action","orgRef","collection","doc","configured","get","Array","isArray","includes","update","enabledPlugins","FieldValue","arrayUnion","orgPin","hostPin","Promise","all","exists","arrayRemove","forgetUndeclaredConnections","options","pinConnections","reconcileListingConnections","previous","previousPin","length","hostIds","scope","where","docs","map","id","error","console","announcePinChange","result","reason","plugins","complete","PIN_DROP_LOST_ERROR","installPluginHandler","req","res","method","status","json","String","body","requestedVersion","version","undefined","authorization","headers","idToken","startsWith","slice","listing","membership","versionData","decoded","app","auth","verifyIdToken","uid","permissions","installPlugins","hostRef","hostSnapshot","pinRef","pinSnapshot","pinExisted","pinnedVersion","delete","data","told","listingRef","artifactType","from","to","runTransaction","tx","snapshot","current","Number","activeInstalls","Math","max","catch","ok","listingSnapshot","deletedAt","hiddenAt","profileId","ownsListing","publisherOrgId","targetOrgId","priceUsd","unpaid","buyerUid","buyerOrgId","revocation","reviewState","approvedSnapshot","orderBy","limit","candidates","publishedAt","newestVersion","newest","find","entry","fallback","latestVersion","chosen","sha256","manifest","now","serverTimestamp","installRef","pinOrgId","leftOutHostIds","runsOn","choice","mode","member","written","pluginId","installing","label","displayName","trim","mayChangeSite","siteId","refusedOnSite","siteName","problem","existing","pin","objectPath","installedFrom","installedAt","updatedAt","set","mergeFields","Object","keys","installCount","increment","manifestHostAbi","hostAbi","hostAbiWarning","isInteger","installed","upgraded"],"mappings":"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"}