@aglyn/plugins-marketplace 1.0.0-beta.264 → 1.0.0-beta.268
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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/plugins-marketplace",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.268",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,25 +25,25 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/aglyn": "1.0.0-beta.
|
|
29
|
-
"@aglyn/aglyn-markdown-editor": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-data-enums": "1.0.0-beta.
|
|
31
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
32
|
-
"@aglyn/shared-ui-jsx": "1.0.0-beta.
|
|
33
|
-
"@aglyn/shared-ui-next": "1.0.0-beta.
|
|
34
|
-
"@aglyn/shared-ui-snackstack": "1.0.0-beta.
|
|
35
|
-
"@aglyn/shared-util-email": "1.0.0-beta.
|
|
36
|
-
"@aglyn/shared-util-http": "1.0.0-beta.
|
|
37
|
-
"@aglyn/shared-util-timestamp": "1.0.0-beta.
|
|
38
|
-
"@aglyn/tenant-data-admin": "1.0.0-beta.
|
|
39
|
-
"@aglyn/tenant-feature-instance": "1.0.0-beta.
|
|
40
|
-
"@aglyn/tenant-runtime": "1.0.0-beta.
|
|
28
|
+
"@aglyn/aglyn": "1.0.0-beta.268",
|
|
29
|
+
"@aglyn/aglyn-markdown-editor": "1.0.0-beta.268",
|
|
30
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.268",
|
|
31
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.268",
|
|
32
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.268",
|
|
33
|
+
"@aglyn/shared-ui-next": "1.0.0-beta.268",
|
|
34
|
+
"@aglyn/shared-ui-snackstack": "1.0.0-beta.268",
|
|
35
|
+
"@aglyn/shared-util-email": "1.0.0-beta.268",
|
|
36
|
+
"@aglyn/shared-util-http": "1.0.0-beta.268",
|
|
37
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.268",
|
|
38
|
+
"@aglyn/tenant-data-admin": "1.0.0-beta.268",
|
|
39
|
+
"@aglyn/tenant-feature-instance": "1.0.0-beta.268",
|
|
40
|
+
"@aglyn/tenant-runtime": "1.0.0-beta.268",
|
|
41
41
|
"@swc/helpers": "0.5.23"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"@mui/material": "^9.3.1",
|
|
45
45
|
"@mui/x-data-grid": "^9.12.0",
|
|
46
|
-
"firebase": "^
|
|
46
|
+
"firebase": "^13.0.0",
|
|
47
47
|
"firebase-admin": "^14.3.0",
|
|
48
48
|
"next": "^16.3.3",
|
|
49
49
|
"react": "^19.2.8"
|
|
@@ -1096,7 +1096,7 @@ async function handler(request) {
|
|
|
1096
1096
|
const liveInstalls = Number((_versionSnapshot_get3 = versionSnapshot.get('activeInstalls')) != null ? _versionSnapshot_get3 : 0);
|
|
1097
1097
|
const stranded = !approving && liveInstalls > 0;
|
|
1098
1098
|
if (listing.profileId) {
|
|
1099
|
-
var _listing_displayName;
|
|
1099
|
+
var _listing_displayName, _listing_displayName1;
|
|
1100
1100
|
const verdict = versionVerdictBody({
|
|
1101
1101
|
approving,
|
|
1102
1102
|
name: String((_listing_displayName = listing.displayName) != null ? _listing_displayName : ''),
|
|
@@ -1115,7 +1115,13 @@ async function handler(request) {
|
|
|
1115
1115
|
orgId: String(listing.profileId),
|
|
1116
1116
|
link: '/'
|
|
1117
1117
|
}).catch(()=>undefined);
|
|
1118
|
-
await emailPublisher(String(listing.profileId), approving ? `${listing.displayName} v${version} passed review` : `${listing.displayName} v${version} was not approved`, verdict
|
|
1118
|
+
await emailPublisher(String(listing.profileId), approving ? `${listing.displayName} v${version} passed review` : `${listing.displayName} v${version} was not approved`, verdict, {
|
|
1119
|
+
frame: {
|
|
1120
|
+
plugin: String((_listing_displayName1 = listing.displayName) != null ? _listing_displayName1 : ''),
|
|
1121
|
+
version,
|
|
1122
|
+
outcome: approving ? 'Approved' : 'Not approved'
|
|
1123
|
+
}
|
|
1124
|
+
});
|
|
1119
1125
|
}
|
|
1120
1126
|
return Response.json({
|
|
1121
1127
|
ok: true,
|
|
@@ -1234,13 +1240,13 @@ async function handler(request) {
|
|
|
1234
1240
|
at: FieldValue.serverTimestamp()
|
|
1235
1241
|
});
|
|
1236
1242
|
if (listing.profileId) {
|
|
1237
|
-
var
|
|
1243
|
+
var _listing_displayName2, _listing_displayName3;
|
|
1238
1244
|
await notifyOrgAdmins(String(listing.profileId), {
|
|
1239
1245
|
type: 'marketplace.review',
|
|
1240
1246
|
// Stopped on every site that runs it, which is an outage for them.
|
|
1241
1247
|
level: revoking ? 'critical' : 'success',
|
|
1242
1248
|
title: revoking ? `"${listing.displayName}" v${version} was stopped` : `"${listing.displayName}" v${version} was allowed to run again`,
|
|
1243
|
-
body: revoking ? `${pluginAndVersion(String((
|
|
1249
|
+
body: revoking ? `${pluginAndVersion(String((_listing_displayName2 = listing.displayName) != null ? _listing_displayName2 : ''), version)} ` + 'has been stopped on every site running it. Those sites now ' + 'show a placeholder where the plugin was.' : `${pluginAndVersion(String((_listing_displayName3 = listing.displayName) != null ? _listing_displayName3 : ''), version)} ` + 'is allowed to run again, and sites running it are no longer ' + 'blocked.',
|
|
1244
1250
|
orgId: String(listing.profileId),
|
|
1245
1251
|
link: '/'
|
|
1246
1252
|
}).catch(()=>undefined);
|
|
@@ -1441,14 +1447,14 @@ async function handler(request) {
|
|
|
1441
1447
|
const publisherOrgId = String((_listing_profileId1 = listing.profileId) != null ? _listing_profileId1 : '');
|
|
1442
1448
|
const publisherSlug = publisherOrgId ? (await firestore.collection('orgs').doc(publisherOrgId).get()).get('slug') : undefined;
|
|
1443
1449
|
if (publisherOrgId) {
|
|
1444
|
-
var
|
|
1450
|
+
var _listing_displayName4, _listing_latestVersion3, _listing_latestApprovedVersion2, _listing_displayName5;
|
|
1445
1451
|
const isPrivate = isPrivateListing(listing);
|
|
1446
1452
|
// One body for the feed and the email (AGL-3432): it names the
|
|
1447
1453
|
// plugin and what happened to it, and a rejection carries its
|
|
1448
1454
|
// category and what to fix (AGL-977).
|
|
1449
1455
|
const verdict = listingVerdictBody({
|
|
1450
1456
|
action,
|
|
1451
|
-
name: String((
|
|
1457
|
+
name: String((_listing_displayName4 = listing.displayName) != null ? _listing_displayName4 : ''),
|
|
1452
1458
|
version: String((_listing_latestVersion3 = listing.latestVersion) != null ? _listing_latestVersion3 : ''),
|
|
1453
1459
|
offeredVersion: String((_listing_latestApprovedVersion2 = listing.latestApprovedVersion) != null ? _listing_latestApprovedVersion2 : ''),
|
|
1454
1460
|
isPrivate,
|
|
@@ -1465,7 +1471,12 @@ async function handler(request) {
|
|
|
1465
1471
|
orgSlug: publisherSlug
|
|
1466
1472
|
}) : '/'
|
|
1467
1473
|
}).catch(()=>undefined);
|
|
1468
|
-
await emailPublisher(publisherOrgId, action === 'reject' ? `${listing.displayName} was rejected — ${rejectionHeadline(category, reason)}` : action === 'delist' ? isPrivate ? `${listing.displayName} is back in review` : `${listing.displayName} was removed from the marketplace` : `${listing.displayName} is now ${nextStatus}`, verdict
|
|
1474
|
+
await emailPublisher(publisherOrgId, action === 'reject' ? `${listing.displayName} was rejected — ${rejectionHeadline(category, reason)}` : action === 'delist' ? isPrivate ? `${listing.displayName} is back in review` : `${listing.displayName} was removed from the marketplace` : `${listing.displayName} is now ${nextStatus}`, verdict, {
|
|
1475
|
+
frame: {
|
|
1476
|
+
plugin: String((_listing_displayName5 = listing.displayName) != null ? _listing_displayName5 : ''),
|
|
1477
|
+
outcome: action === 'reject' ? 'Rejected' : action === 'delist' ? isPrivate ? 'Back in review' : 'Removed from the marketplace' : `Now ${nextStatus}`
|
|
1478
|
+
}
|
|
1479
|
+
});
|
|
1469
1480
|
}
|
|
1470
1481
|
}
|
|
1471
1482
|
await addAdminAudit(firestore, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/server/admin-reviews.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 {\n REVOKED_VERSION_REVIEW_STATE,\n revocationWithdrawsReviewedClaim,\n shouldStripVerifiedOnTakedown,\n VERIFIED_STRIPPED_STATUS,\n} from '../model/plugin-review-status'\nimport {\n checkPluginBundle,\n isPluginRevoked,\n newestInstallableVersion,\n isStoredVerdictCurrent,\n nextRevocationState,\n PLUGIN_HOST_ABI_VERSION,\n PLUGIN_VERIFIER_VERSION,\n pluginArtifactPath,\n pluginRequestFromWeb,\n readPluginContributions,\n type PluginRevocation,\n type StoredBundleVerdict,\n} from '@aglyn/aglyn/server'\nimport {\n emailUnverifiedResponse,\n findUserByUidAcrossPools,\n firebaseAdmin,\n isImpersonationSession,\n updateExisting,\n} from '@aglyn/tenant-data-admin'\nimport { invalidIdTokenResponse } from '@aglyn/tenant-data-admin/server/id-token-refusal'\nimport { buildRoute, Route } from '@aglyn/aglyn/server'\nimport {\n outstandingChecklistItems,\n PLUGIN_REVIEW_CHECKLIST,\n} from '../model/plugin-review-checklist'\nimport {\n pluginRejectionCategory,\n rejectionHeadline,\n rejectionInputError,\n} from '../model/plugin-rejection-categories'\nimport { attestationsForBytes } from '../model/publisher-attestation'\nimport { VERIFICATION_DECLINE_COOLDOWN_DAYS } from '../model/listing-verification'\nimport { isPrivateListing } from '../model/listing-visibility'\nimport {\n PUBLISHER_AGREEMENT_VERSION,\n publisherAgreementState,\n} from '../model/publisher-agreement'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport { notifyOrgAdmins } from '@aglyn/tenant-data-admin'\nimport { refreshListingQueryFields } from './listing-query-fields'\nimport { nameSearchNormalizers } from '@aglyn/aglyn/app-utils/name-search'\nimport {\n type ListQueryPlan,\n planListQuery,\n} from '@aglyn/shared-ui-jsx/const/list-query-plan'\nimport { applyListQuery } from '@aglyn/tenant-data-admin/server/list-query'\nimport {\n REVIEW_QUEUE_QUERY,\n type ReviewQueueSection,\n reviewQueueBase,\n reviewQueueStatusClauses,\n} from '../model/listing-query'\n/*\n * The publisher fan-out lives beside the usage-alert one in `_lib` rather\n * than here. Both are multi-recipient platform sends that have to read the\n * platform suppression list before addressing anybody, and that list has to\n * be reachable by a spec that does not drag the admin SDK in behind a route.\n */\nimport { emailPublisher } from './publisher-review-email'\nimport { dropCachesForListing } from './revoke-cache-drop'\nimport { addAdminAudit } from '@aglyn/tenant-data-admin/server/admin-audit-write'\nimport { compareArtifactVersions } from '../model/update-state'\n\n/**\n * Marketplace review queue (AGL-432) — Strapi Market's two-phase review\n * as staff tooling. GET returns plugin listings awaiting review\n * (submitted/in_review) with the static-verifier verdict re-run against\n * the stored artifact (the same AGL-426 checks the publish API enforced —\n * re-run here so a reviewer sees them without trusting the publish-time\n * result). POST moves a listing through the lifecycle:\n *\n * start-review → in_review list → listed (publicly browsable)\n * verify → verified (✅ badge) reject → rejected (+ reason,\n * notification to the publisher)\n *\n * Realm trust stays a SEPARATE, super-staff grant (`admin-trust.ts`) —\n * listing/verifying here never signs anything. Every action lands in\n * adminAudit.\n */\n/** Every id the checklist route will accept (AGL-963). */\nconst REQUIRED_OR_OPTIONAL = new Set(\n PLUGIN_REVIEW_CHECKLIST.map((item) => item.id),\n)\n\nconst ACTIONS: Record<string, string> = {\n 'start-review': 'in_review',\n list: 'listed',\n verify: 'verified',\n reject: 'rejected',\n // Moves BACK down the ladder (AGL-965). Review used to be a one-way\n // ratchet: the only retreats were `reject`, which reads as a verdict on\n // the whole listing and notifies the publisher as such, and takedown,\n // which is the kill switch and stops the plugin in every workspace that\n // already installed it. Neither expresses \"pull it from the marketplace\n // while we look again, without breaking the sites already using it\".\n delist: 'in_review',\n unverify: 'listed',\n}\n\n/*\n * THE WORDS A PUBLISHER READS ABOUT A VERDICT (AGL-3432).\n *\n * The publisher email (`plugin-review-update`) prints only its body, never its\n * subject, and the console feed shows the body under a short title. So every\n * body below opens by naming the plugin, the version when there is one, and\n * what happened to it. A publisher with several plugins cannot tell from\n * \"Your plugin\" which one review means.\n *\n * A private listing never reaches the marketplace (AGL-968), so its copy never\n * says it entered or left it.\n */\n\n/** \"Acme Forms version 1.2.0\", or just the name when no version is known. */\nfunction pluginAndVersion(name: string, version: string): string {\n return version ? `${name} version ${version}` : name\n}\n\n/**\n * Why it was turned down: the category and what to do about it, then the\n * reviewer's comment when there is one (AGL-977).\n *\n * A comment is required only for \"Other\", so every other rejection stands on\n * its category: printing the optional comment alone would leave most\n * rejections with a blank reason and nothing to fix. \"Other\" says nothing by\n * itself, so there the comment is the reason.\n */\nfunction rejectionReasonText(category: string, reason: string): string {\n const picked = pluginRejectionCategory(category)\n const comment = reason.trim()\n if (!picked || picked.requiresComment) return comment\n const because = `${picked.label}. ${picked.guidance}`\n return comment ? `${because}\\n\\nReviewer’s comment: ${comment}` : because\n}\n\n/** The body of a version verdict, for the console notification and the email. */\nfunction versionVerdictBody(input: {\n approving: boolean\n name: string\n version: string\n /** The version new installs receive once this verdict has landed. */\n offeredVersion: string\n /** This approval is what put the listing in the marketplace. */\n listedNow: boolean\n category: string\n reason: string\n liveInstalls: number\n}): string {\n const { name, version, offeredVersion, liveInstalls } = input\n const subject = pluginAndVersion(name, version)\n if (input.approving) {\n // Approving an OLDER version never walks the offer back, so \"now the\n // version new installs receive\" is said only when it became so.\n const offer =\n offeredVersion === version\n ? `It is now the version new installs of ${name} receive. Sites ` +\n 'already running it stay on the version they have until their ' +\n 'owners upgrade.'\n : offeredVersion\n ? `New installs still receive version ${offeredVersion}, the ` +\n 'newest approved version.'\n : ''\n return [\n `${subject} passed review.${offer ? ` ${offer}` : ''}`,\n input.listedNow ? `${name} is now listed in the marketplace.` : '',\n ]\n .filter(Boolean)\n .join('\\n\\n')\n }\n const why = rejectionReasonText(input.category, input.reason)\n return [\n `${subject} was not approved${why ? `: ${why}` : '.'}`,\n `Publishing a new version puts ${name} back in the review queue. The ` +\n 'previously approved version, if any, keeps installing in the meantime.',\n // Pins sitting on these bytes (AGL-1085). The publisher cannot uninstall\n // a version from somebody else's site, so this says what happens rather\n // than telling them to do it.\n liveInstalls > 0\n ? `Version ${version} is still running on ${liveInstalls} ` +\n `site${liveInstalls === 1 ? '' : 's'}. A rejection stops new ` +\n 'installs; it does not remove the version from sites already ' +\n 'running it.'\n : '',\n ]\n .filter(Boolean)\n .join('\\n\\n')\n}\n\n/** The body of a listing verdict, for the console notification and the email. */\nfunction listingVerdictBody(input: {\n action: string\n name: string\n /** The listing's newest bytes, the ones the reviewer read. */\n version: string\n /** The version new installs receive. */\n offeredVersion: string\n isPrivate: boolean\n category: string\n reason: string\n}): string {\n const { name, offeredVersion, isPrivate } = input\n const subject = pluginAndVersion(name, input.version)\n const comment = input.reason.trim()\n const offer = offeredVersion\n ? ` New installs receive version ${offeredVersion}.`\n : ''\n switch (input.action) {\n case 'reject': {\n // A rejection is not a delist, comment or no comment: nobody looks\n // again until the publisher fixes it and sends a new version, so it\n // never borrows the delist's \"while we review it again\".\n const why = rejectionReasonText(input.category, input.reason)\n const where = isPrivate ? 'in review' : 'from the marketplace'\n const meanwhile = isPrivate\n ? 'Sites that already installed it keep working.'\n : `${name} is out of the marketplace while it is rejected; sites ` +\n 'that already installed it keep working.'\n return [\n `${subject} was rejected ${where}${why ? `: ${why}` : '.'}`,\n `${meanwhile} To send it back for review, publish a new version.`,\n ].join('\\n\\n')\n }\n case 'delist':\n return [\n isPrivate\n ? `We moved ${subject} back into review. Sites that already ` +\n 'installed it keep working.'\n : `We removed ${subject} from the marketplace while we review it ` +\n 'again. Sites that already installed it keep working.',\n comment ? `Reviewer’s comment: ${comment}` : '',\n ]\n .filter(Boolean)\n .join('\\n\\n')\n case 'verify':\n return isPrivate\n ? `${name} is now verified.`\n : `${name} is now verified: its marketplace listing shows the ` +\n 'verified badge.'\n default:\n return isPrivate\n ? `${name} passed review. It is a private plugin, so it stays out ` +\n `of the marketplace and only your workspace can install it.${offer}`\n : `${name} passed review and is now listed in the marketplace.${offer}`\n }\n}\n\n/**\n * One listing in full, for the detail page (AGL-959). The index deals in\n * rows; everything a reviewer needs to actually decide — manifest,\n * capabilities, ABI, verifier findings, per-version trust, takedown state —\n * is assembled here rather than smeared across the list payload.\n */\nasync function listingDetail(\n firestore: FirebaseFirestore.Firestore,\n listingId: string,\n requestedVersion?: string,\n): Promise<Response> {\n const listingRef = firestore.collection('marketplaceListings').doc(listingId)\n const snapshot = await listingRef.get()\n const listing = snapshot.data()\n if (!listing) {\n return Response.json({ error: 'Unknown listing' }, { status: 404 })\n }\n\n const versionsSnapshot = await listingRef\n .collection('pluginVersions')\n .orderBy('publishedAt', 'desc')\n .limit(25)\n .get()\n const versions = versionsSnapshot.docs.map((doc) => {\n const publishedAt = doc.get('publishedAt')\n return {\n version: String(doc.get('version') ?? doc.id),\n trust: doc.get('trust') ?? null,\n sha256: String(doc.get('sha256') ?? ''),\n hostAbi: Number(doc.get('manifest.hostAbi')) || null,\n capabilities: doc.get('manifest.capabilities') ?? {},\n // What these bytes declare they contribute (AGL-3116); `null` when the\n // version predates the contract and loads under its default.\n contributes: readPluginContributions(doc.get('manifest.contributes')) ?? null,\n // Declared canvas elements (AGL-1031), for THESE bytes. Publisher copy\n // that renders in the customer's element picker and nowhere on the\n // listing page — so without it here, the `element-metadata` criterion\n // asks a reviewer to check text this page never shows (AGL-2486).\n elements: doc.get('manifest.elements') ?? [],\n publishedAt: publishedAt?.toDate?.()?.toISOString() ?? null,\n // Per-version installs (AGL-1036). \"How many workspaces would a revoke\n // actually hit\" is a per-version question, and the listing total shown\n // elsewhere on this page answers a different one.\n installCount: Number(doc.get('installCount') ?? 0),\n activeInstalls: Number(doc.get('activeInstalls') ?? 0),\n signed: Boolean(doc.get('signature')),\n // The repo as declared for THESE bytes (AGL-1076); absent on versions\n // published before it was collected.\n repositoryUrl: String(doc.get('repositoryUrl') ?? ''),\n reviewState: String(doc.get('reviewState') ?? 'pending'),\n grandfathered: Boolean(doc.get('grandfathered')),\n // Server-side only — used to decide whether the verdict below needs\n // recomputing; stripped before the payload goes out.\n verification: doc.get('verification') ?? null,\n reviewChecklist: doc.get('reviewChecklist') ?? null,\n publisherAttestation: doc.get('publisherAttestation') ?? null,\n }\n })\n\n // Publisher is an ORG since AGL-652 — show the name, not the raw id.\n const publisherId = String(listing.profileId ?? '')\n const publisher = publisherId\n ? await firestore.collection('orgs').doc(publisherId).get()\n : null\n\n // Which terms this publisher is actually under (AGL-1077). A reviewer\n // weighing a takedown, a delist, or a revocation is deciding what we are\n // entitled to do — and that is answered by the agreement the ORG accepted,\n // not by the per-version attestation beside it.\n const publisherProfile = publisherId\n ? await firestore.collection('publisherProfiles').doc(publisherId).get()\n : null\n const acceptance = publisherProfile?.get('publisherAgreement') as\n | { version?: string; acceptedBy?: string; acceptedAt?: { toDate?: () => Date } }\n | undefined\n\n // Kill switch, so the page can say whether the plugin is actually stopped\n // rather than only de-listed (AGL-948).\n const revocation = (\n await firestore.collection('revocations').doc(listingId).get()\n ).data()\n\n // The verifier verdict for the version under review (AGL-426), served\n // from the version doc when it is current (AGL-962).\n //\n // `checkPluginBundle` is pure and the artifact is immutable and\n // content-addressed, so a verdict pinned to {sha256, verifierVersion} is\n // exactly as trustworthy as re-running it — and re-running meant\n // downloading up to a megabyte from Storage on EVERY page view. A stored\n // verdict from an older checker, or for different bytes, is ignored.\n // Which version this page is reviewing (AGL-966). Defaults to the oldest\n // version still awaiting a verdict — that is the work — falling back to\n // the latest when everything is decided. A reviewer can pin any version\n // explicitly; the checklist and verifier verdict follow the selection,\n // because both are statements about specific bytes.\n const pendingFirst = [...versions]\n .reverse()\n .find((entry) => entry.reviewState !== 'approved')\n const reviewVersion =\n (requestedVersion &&\n versions.find((entry) => entry.version === requestedVersion)?.version) ||\n pendingFirst?.version ||\n String(listing.latestVersion ?? '')\n const artifactsBucket = process.env.PLUGIN_ARTIFACTS_BUCKET\n const reviewEntry = versions.find((entry) => entry.version === reviewVersion)\n const reviewSha = reviewEntry?.sha256\n const stored = reviewEntry?.verification as StoredBundleVerdict | undefined\n const storedIsCurrent = isStoredVerdictCurrent(stored, reviewSha ?? '')\n\n let verifier: unknown = storedIsCurrent\n ? {\n ok: stored?.ok,\n problems: stored?.problems ?? [],\n // The per-check summary rides along in the stored verdict (AGL-1087)\n // so the page can show what was checked without re-downloading a\n // megabyte of artifact on every view.\n checks: stored?.checks ?? [],\n }\n : null\n let verifierCached = storedIsCurrent\n if (!storedIsCurrent && artifactsBucket && reviewSha) {\n try {\n const [bytes] = await firebaseAdmin\n .app()\n .storage()\n .bucket(artifactsBucket)\n .file(pluginArtifactPath(listingId, reviewVersion, reviewSha))\n .download()\n // The manifest for THESE bytes, so an undeclared network call is a\n // finding rather than a warning (AGL-964). Read from the version doc,\n // never from the listing — capabilities change between versions.\n const result = checkPluginBundle(bytes.toString('utf8'), {\n declaredNetwork: Array.isArray(reviewEntry?.capabilities?.network)\n ? reviewEntry.capabilities.network.map((origin: unknown) =>\n String(origin),\n )\n : [],\n // Compared, never required: a version published before the contract\n // loads under its default, and review must not refuse it for a rule\n // it was never held to.\n declaredContributions: reviewEntry?.contributes ?? null,\n })\n verifier = result\n // Write it back so this sha is verified at most once platform-wide —\n // covers every listing published before the verdict was persisted at\n // publish time, and any version whose checker has since moved on.\n // Best effort: a failed write costs the next reader a re-run, nothing\n // more, so it must never fail the page.\n await listingRef\n .collection('pluginVersions')\n .doc(reviewVersion)\n .set(\n {\n verification: {\n ok: result.ok,\n problems: result.problems,\n checks: result.checks,\n sha256: reviewSha,\n verifierVersion: PLUGIN_VERIFIER_VERSION,\n checkedAt: FieldValue.serverTimestamp(),\n },\n },\n { merge: true },\n )\n .catch(() => undefined)\n } catch {\n verifier = { error: 'artifact unavailable' }\n verifierCached = false\n }\n }\n\n // Who attested, by name (AGL-969). \"A named publisher said this on a date\"\n // is the half that makes a false attestation actionable, and a raw uid\n // names nobody. Best effort — a deleted account must not blank the page.\n const attestationEntries = Object.values(\n (reviewEntry?.publisherAttestation ?? {}) as Record<\n string,\n { by?: string; at?: { toDate?: () => Date }; sha256?: string }\n >,\n ).filter((entry) => entry?.sha256 === reviewSha)\n const attesterUid = attestationEntries.find((entry) => entry.by)?.by\n const attestedAt =\n attestationEntries.find((entry) => entry.at)?.at?.toDate?.()?.toISOString() ??\n null\n let attestedBy: string | null = attesterUid ?? null\n if (attesterUid) {\n try {\n // Across pools (AGL-1122): an SSO attester is invisible to project-level\n // `getUser`, so this fell through to the catch and the review page named\n // the attester by raw uid — indistinguishable from \"account deleted\".\n const found = await findUserByUidAcrossPools(attesterUid)\n if (found) {\n attestedBy =\n found.record.email ?? found.record.displayName ?? attesterUid\n }\n } catch {\n // Account gone — the uid still identifies the statement.\n }\n }\n\n return Response.json(\n {\n listingId,\n displayName: listing.displayName ?? listingId,\n description: listing.description ?? '',\n // The publisher's ask, so the page can offer Decline beside Verify\n // (AGL-1217). Serialized: `decidedAt`/`requestedAt` are Timestamps and\n // this payload crosses JSON.\n verificationRequest: listing.verificationRequest\n ? {\n state: String(listing.verificationRequest.state ?? ''),\n requestedAt:\n listing.verificationRequest.requestedAt?.toDate?.()?.toISOString() ??\n null,\n declineReason: listing.verificationRequest.declineReason ?? null,\n }\n : null,\n readme: listing.readme ?? '',\n license: listing.license ?? '',\n categories: listing.categories ?? [],\n homepageUrl: listing.homepageUrl ?? '',\n // Prefer what the version under review declared (AGL-1076). The\n // listing carries whatever the most recent publish said, and a repo\n // can move between versions — the link a reviewer opens should be the\n // one attested against the bytes in front of them. Falls back to the\n // listing for versions published before this was collected.\n repositoryUrl: reviewEntry?.repositoryUrl || listing.repositoryUrl || '',\n publisherId,\n publisherName: publisher?.get('name') ?? publisherId,\n publisherSlug: publisher?.get('slug') ?? null,\n reviewStatus: listing.reviewStatus ?? 'submitted',\n rejectionReason: listing.rejectionReason ?? '',\n rejectionCategory: listing.rejectionCategory ?? '',\n priceUsd: Number(listing.priceUsd ?? 0),\n latestVersion: String(listing.latestVersion ?? ''),\n // The version the checklist, verifier verdict and approve/reject\n // actions on this payload refer to.\n reviewVersion,\n activeInstalls: Number(listing.activeInstalls ?? 0),\n hidden: Boolean(listing.hiddenAt),\n hiddenReason: String(listing.hiddenReason ?? ''),\n revoked: Boolean(revocation),\n revokedVersions: revocation?.versions ?? null,\n unpublished: Boolean(listing.deletedAt),\n private: listing.visibility === 'private',\n platformHostAbi: PLUGIN_HOST_ABI_VERSION,\n // Where the bundle bytes actually live (AGL-990). Staff-only payload,\n // and a bucket NAME is not a credential — reaching the object still\n // needs a Google account with access. Without it the page cannot build\n // a Cloud console link, and this bucket does not appear in the Firebase\n // console at all, so a reviewer has no way to find it by hand.\n artifactsBucket: artifactsBucket ?? null,\n versions: versions.map(\n // The rest-strip is the point: `verification`, `reviewChecklist` and\n // `publisherAttestation` are named only so they DO NOT reach the\n // client payload, which is what the disable below is for. The\n // duplicate directive that used to sit above `versions.map(` covered\n // a line with nothing unused on it, and this config treats an\n // unused disable as a failure.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n ({ verification, reviewChecklist, publisherAttestation, ...entry }) => ({\n ...entry,\n // Per-version kill-switch state (AGL-1085), read through the same\n // helper the loaders use so the page cannot disagree with the\n // runtime about whether these bytes are stopped.\n revoked: isPluginRevoked(\n revocation as PluginRevocation | undefined,\n entry.version,\n ),\n }),\n ),\n // Review checklist for the version under review (AGL-963). Ticks made\n // against different bytes do not count, so a republish starts clean.\n checklist: Object.fromEntries(\n Object.entries(\n (reviewEntry?.reviewChecklist ?? {}) as Record<\n string,\n { by?: string; sha256?: string }\n >,\n )\n .filter(([, entry]) => entry?.sha256 === reviewSha)\n .map(([id, entry]) => [id, { by: entry.by ?? null }]),\n ),\n checklistOutstanding: outstandingChecklistItems(\n reviewEntry?.reviewChecklist as never,\n reviewSha ?? '',\n ),\n // What the publisher stated about THESE bytes (AGL-969). Not a review\n // input — a reviewer still checks everything themselves — but it says\n // what was claimed, so a discrepancy is a documented false statement\n // rather than a difference of opinion. Attestations recorded against\n // other bytes are dropped, exactly like staff ticks.\n attestation: attestationsForBytes(\n reviewEntry?.publisherAttestation as never,\n reviewSha ?? '',\n ),\n attestedBy,\n attestedAt,\n // The publisher agreement this ORG is under (AGL-1077). `current` is\n // computed here rather than shipped as a boolean so the page can say\n // \"on an older version\" — which is a different thing from never\n // having accepted, and matters differently to a takedown decision.\n publisherAgreement: {\n version: acceptance?.version ?? null,\n acceptedAt: acceptance?.acceptedAt?.toDate?.()?.toISOString() ?? null,\n required: PUBLISHER_AGREEMENT_VERSION,\n state: publisherAgreementState(acceptance),\n },\n verifier,\n verifierCached,\n verifierVersion: PLUGIN_VERIFIER_VERSION,\n },\n { status: 200 },\n )\n}\n\n/**\n * Re-derive `latestApprovedVersion` from the versions themselves (AGL-2306).\n *\n * The mirror was written on approval only, with a comment saying it \"only ever\n * moves forward\" — true of approvals, and the reason it is there. What nothing\n * did was move it BACK. A version approved and later rejected on re-review, or\n * killed with the per-version switch, left the listing still advertising it:\n * `host-plugins-card` and `model/update-state` read this field to offer\n * \"Update to vX\", and `install-plugin` would then refuse those exact bytes.\n *\n * Contained, because `install-plugin` recomputes approval live and never\n * trusts this field — no unreviewed or revoked bytes could ship — but it is\n * the AGL-1016 guarantee leaking back out through a badge, which is where\n * AGL-966 came in.\n *\n * Recomputed rather than decremented: \"the newest version that is approved AND\n * not revoked\" is a property of the whole set, and stepping it back one at a\n * time is how a mirror drifts in the other direction.\n *\n * Revoked versions are excluded because the offer has to be installable, and\n * `install-plugin` 409s on a revoked pin. That also makes un-revoking restore\n * the offer, which is why this runs on both revoke and unrevoke.\n *\n * Deletes the field when nothing qualifies: an absent mirror reads as \"no\n * update to offer\", while a stale string reads as an update that does not\n * exist.\n */\nasync function repairLatestApprovedVersion(\n listingRef: FirebaseFirestore.DocumentReference,\n revocation: PluginRevocation | null | undefined,\n): Promise<void> {\n const versions = await listingRef\n .collection('pluginVersions')\n .orderBy('publishedAt', 'desc')\n .limit(50)\n .get()\n // The DECISION lives in core (`newestInstallableVersion`), where it is\n // testable without a Firestore double and shared with anything else that\n // has to answer \"what is on offer\". This function is only the query.\n const newest = newestInstallableVersion(\n versions.docs.map((doc) => ({\n version: doc.id,\n reviewState: doc.get('reviewState'),\n })),\n revocation,\n compareArtifactVersions,\n )\n await updateExisting(listingRef, {\n latestApprovedVersion: newest ?? FieldValue.delete(),\n updatedAt: FieldValue.serverTimestamp(),\n })\n}\n\n/** Rows a queue section reads before it says there are more. */\nconst REVIEW_QUEUE_PAGE = 100\n/** The most a reviewer can page the queue out to. */\nconst REVIEW_QUEUE_MAX = 1000\n\nconst queryParam = (value: unknown): string =>\n String(Array.isArray(value) ? (value[0] ?? '') : (value ?? ''))\n\n/**\n * The staff review index (AGL-961), every section on its query (AGL-3321).\n *\n * Three sections, each its own query over live plugin listings:\n *\n * - Awaiting review: the newest bytes have no approval. Read from the\n * listing's `latestVersionReviewState` summary, whose `pending` and\n * `rejected` are exactly \"the version's own `reviewState` is not\n * `approved`\" — `revoked` is the one summary value a version keeps its\n * `approved` under, and it is not in `AWAITING_REVIEW_STATES`, so a killed\n * version never re-enters the queue looking unread (AGL-1121).\n * - Listed: `reviewStatus` listed or verified.\n * - Verification requested: the publisher's standing ask (AGL-1217).\n *\n * The Status select and the search (`?status=`, `?q=`) are clauses on every\n * section's query, planned once by `REVIEW_QUEUE_QUERY`; search reads the\n * listing name's `nameTokens`. It used to be matched over one window of a\n * hundred plugin listings, so a plugin past it was in no section at all.\n * `?limit=` pages each section out; a section that holds more than it read\n * says so rather than presenting a short list as the whole queue.\n */\nasync function reviewQueue(\n firestore: FirebaseFirestore.Firestore,\n query: Record<string, unknown>,\n): Promise<Response> {\n const clauses = reviewQueueStatusClauses(queryParam(query['status']))\n const words = queryParam(query['q']).split(/\\s+/).filter(Boolean)\n const asked = Math.floor(Number(queryParam(query['limit'])))\n const pageLimit = Number.isFinite(asked) && asked > 0\n ? Math.min(asked, REVIEW_QUEUE_MAX)\n : REVIEW_QUEUE_PAGE\n const listings = firestore.collection('marketplaceListings')\n\n let refused: ListQueryPlan['refused'] = []\n let notices: string[] = []\n const more: Record<ReviewQueueSection, boolean> = {\n queue: false,\n listed: false,\n verification: false,\n }\n const read = async (section: ReviewQueueSection) => {\n const base = reviewQueueBase(section, clauses)\n // A Status the section cannot hold: empty by definition, never queried.\n if (!base) return []\n const plan = planListQuery(\n REVIEW_QUEUE_QUERY,\n { clauses, search: words, base },\n nameSearchNormalizers,\n )\n refused = plan.refused\n notices = plan.notices\n const snapshot = await applyListQuery(listings, plan)\n .limit(pageLimit + 1)\n .get()\n more[section] = snapshot.docs.length > pageLimit\n return snapshot.docs.slice(0, pageLimit)\n }\n const [queueDocs, listedDocs, verificationDocs] = await Promise.all([\n read('queue'),\n read('listed'),\n read('verification'),\n ])\n\n // Rows only (AGL-961). This used to DOWNLOAD every queued bundle to re-run\n // the verifier, so the index paid a Storage round trip per submission\n // before it could paint. The verifier now runs on the detail page, for the\n // one listing being read.\n const reviewRow = async (doc: FirebaseFirestore.QueryDocumentSnapshot) => {\n const listing = doc.data()\n const version = String(listing.latestVersion ?? '')\n const latest = version\n ? await doc.ref.collection('pluginVersions').doc(version).get()\n : null\n return {\n listingId: doc.id,\n displayName: listing.displayName ?? doc.id,\n description: listing.description ?? '',\n license: listing.license ?? '',\n categories: listing.categories ?? [],\n profileId: listing.profileId,\n reviewStatus: listing.reviewStatus ?? 'submitted',\n priceUsd: Number(listing.priceUsd ?? 0),\n version,\n hidden: Boolean(listing.hiddenAt),\n private: listing.visibility === 'private',\n latestReviewState: String(latest?.get('reviewState') ?? 'pending'),\n grandfathered: Boolean(latest?.get('grandfathered')),\n // The publisher's standing ask for the badge (AGL-1217).\n verificationRequest: listing.verificationRequest ?? null,\n }\n }\n const queue = await Promise.all(queueDocs.map(reviewRow))\n // A THIRD section, deliberately not folded into `queue` (AGL-1217).\n // `queue` means \"these bytes have not been read\"; a verification request\n // means \"this publisher asked us to vouch for who they are\". A listing can\n // sit in both at once, and merging them would hide one behind the other.\n const verificationRequests = await Promise.all(verificationDocs.map(reviewRow))\n // Listed/verified plugins with per-version trust (AGL-885): once a listing\n // leaves the queue the Grant/Revoke realm-trust actions used to leave with\n // it. This keeps every listed plugin's version trust state administrable.\n const listed = await Promise.all(\n listedDocs.map(async (doc) => {\n const listing = doc.data()\n const versionsSnapshot = await doc.ref\n .collection('pluginVersions')\n .orderBy('publishedAt', 'desc')\n .limit(10)\n .get()\n const versions = versionsSnapshot.docs.map((versionDoc) => ({\n version: String(versionDoc.get('version') ?? versionDoc.id),\n trust: versionDoc.get('trust') ?? null,\n reviewState: String(versionDoc.get('reviewState') ?? 'pending'),\n }))\n return {\n listingId: doc.id,\n displayName: listing.displayName ?? doc.id,\n reviewStatus: listing.reviewStatus,\n profileId: listing.profileId,\n latestVersion: String(listing.latestVersion ?? ''),\n // Takedown state (AGL-952), so a row shows the current verdict rather\n // than sending a reviewer into the detail page to find out whether\n // the plugin is stopped.\n hidden: Boolean(listing.hiddenAt),\n hiddenReason: String(listing.hiddenReason ?? ''),\n private: listing.visibility === 'private',\n pendingVersions: versions.filter((entry) => entry.reviewState !== 'approved')\n .length,\n // Summarised for the row; the per-version controls live on the detail\n // page (AGL-960).\n realmVersions: versions.filter((entry) => entry.trust === 'realm').length,\n versionCount: versions.length,\n versions,\n }\n }),\n )\n\n // Publisher names for scanning (AGL-961) — a listing id tells a reviewer\n // nothing. One batched read for every section.\n const publisherIds = [\n ...new Set(\n [...queue, ...listed, ...verificationRequests]\n .map((entry) => String(entry.profileId ?? ''))\n .filter(Boolean),\n ),\n ]\n const publishers: Record<string, string> = {}\n if (publisherIds.length) {\n const docs = await firestore.getAll(\n ...publisherIds.map((id) => firestore.collection('orgs').doc(id)),\n )\n for (const doc of docs) {\n if (doc.exists) publishers[doc.id] = String(doc.get('name') ?? doc.id)\n }\n }\n return Response.json(\n {\n queue,\n listed,\n publishers,\n verificationRequests,\n refused,\n notices,\n more,\n limit: pageLimit,\n },\n { status: 200 },\n )\n}\n\nasync function handler(request: Request): Promise<Response> {\n const { method, body, query, headers: rawHeaders } =\n await pluginRequestFromWeb(request)\n const headers = rawHeaders as Partial<Record<string, string>>\n const authorization = headers.authorization ?? ''\n const idToken = authorization.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length)\n : undefined\n if (!idToken) return Response.json({ error: 'Unauthenticated' }, { status: 401 })\n\n try {\n const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken)\n if (!decoded.email_verified && !isImpersonationSession(decoded)) {\n return emailUnverifiedResponse()\n }\n if (!decoded['staff']) {\n return Response.json({ error: 'Staff only' }, { status: 403 })\n }\n const firestore = firebaseAdmin.app().firestore()\n\n if (method === 'GET') {\n const detailId = String(query['listingId'] ?? '')\n if (detailId) {\n return listingDetail(firestore, detailId, String(query['version'] ?? ''))\n }\n\n return reviewQueue(firestore, query)\n }\n\n if (method !== 'POST') {\n return Response.json({ error: 'Method not allowed' }, { status: 405 })\n }\n const listingId = String(body?.listingId ?? '')\n const action = String(body?.action ?? '')\n\n // Takedown (AGL-658) is separate from the review verdict: it applies to\n // EVERY artifact type, not just plugins, and it must not overwrite a\n // plugin's `reviewStatus` — a hidden plugin that later gets un-hidden\n // should return to the verdict it had, not to square one.\n if (action === 'hide' || action === 'unhide') {\n if (!listingId) {\n return Response.json({ error: 'Missing listingId' }, { status: 400 })\n }\n const hideReason = String(body?.reason ?? '').slice(0, 500)\n if (action === 'hide' && !hideReason.trim()) {\n return Response.json({ error: 'Hiding needs a reason' }, { status: 400 })\n }\n const reviewUid = String(body?.reviewUid ?? '')\n const target = reviewUid\n ? firestore\n .collection('marketplaceListings')\n .doc(listingId)\n .collection('reviews')\n .doc(reviewUid)\n : firestore.collection('marketplaceListings').doc(listingId)\n const targetSnapshot = await target.get()\n if (!targetSnapshot.exists) {\n return Response.json({ error: 'Unknown target' }, { status: 404 })\n }\n // A takedown strips the Verified badge (AGL-1121, decided 2026-08-03).\n //\n // Verified is OUR claim that a human vouched for this publisher. Taking\n // the listing down is us saying the opposite out loud, so the two cannot\n // both stand — a badge surviving a takedown would still be asserting the\n // vouch on the listing page the moment it was restored.\n //\n // Demoted to `listed`, not to `rejected` or `submitted`: the takedown\n // itself is what makes it non-browsable (`hiddenAt`), and rewriting the\n // review verdict would destroy the record of a review that did happen.\n //\n // Restoring deliberately does NOT put the badge back. Re-granting is\n // `verify`, which re-checks the review checklist server-side; an automatic\n // regrant would hand back the badge without anyone re-forming the opinion\n // behind it. Losing it to a takedown is meant to cost a re-review.\n const stripVerified = shouldStripVerifiedOnTakedown({\n action,\n reviewStatus: targetSnapshot.get('reviewStatus'),\n isListingTarget: !reviewUid,\n })\n await target.set(\n reviewUid\n ? { hidden: action === 'hide' }\n : action === 'hide'\n ? {\n hiddenAt: FieldValue.serverTimestamp(),\n hiddenBy: decoded.uid,\n hiddenReason: hideReason,\n ...(stripVerified\n ? { reviewStatus: VERIFIED_STRIPPED_STATUS }\n : {}),\n }\n : {\n hiddenAt: FieldValue.delete(),\n hiddenBy: FieldValue.delete(),\n hiddenReason: FieldValue.delete(),\n },\n { merge: true },\n )\n // A takedown leaves browse for everyone but its publisher, and the\n // queue's Taken down status reads it (AGL-3321).\n if (!reviewUid) await refreshListingQueryFields(target)\n\n // Takedown means takedown (AGL-948). Hiding a PLUGIN listing also\n // writes the kill switch, so one staff action has one outcome\n // instead of leaving the bundle executing in every workspace that\n // already installed it. The realm join blocks on `hiddenAt` too, but\n // the revocation is what the client-side loaders and the public\n // listing-versions endpoint already understand — belt and braces on\n // the one action where a gap means running attacker code.\n const isPlugin =\n !reviewUid &&\n (targetSnapshot.get('artifactType') === 'plugin' ||\n targetSnapshot.get('type') === 'plugin')\n /**\n * An email starter earns the same treatment for the same reason.\n *\n * It is copied on install, so `hiddenAt` alone reaches only the\n * storefront: every tenant who already installed it goes on MAILING it,\n * from the sending domain every other tenant shares. The revocation is\n * what `emailStarterSendBlock` reads at the send, which is the only\n * chokepoint that can still refuse.\n *\n * The five other copied types are deliberately not here. Their content\n * is rendered on the tenant's own site, where `hiddenAt` blocking the\n * next install is the whole of what a takedown can honestly promise —\n * stopping a page from rendering is a different decision, and one nobody\n * has made.\n */\n const killsInstalledCopies =\n isPlugin ||\n (!reviewUid && targetSnapshot.get('artifactType') === 'emailStarter')\n if (killsInstalledCopies) {\n const revocationRef = firestore.collection('revocations').doc(listingId)\n const current = (await revocationRef.get()).data() as\n | PluginRevocation\n | undefined\n // Both transitions go through the shared helper (AGL-1085) so a\n // takedown and a per-version review revocation can coexist. Restoring\n // used to DELETE the doc whenever it was takedown-owned, which also\n // un-revoked any version a reviewer had stopped for an unrelated\n // reason — `reviewVersions` is what survives the round trip.\n const next = nextRevocationState(current, {\n type: action === 'hide' ? 'takedown' : 'restore',\n })\n if (next) {\n await revocationRef.set(\n {\n ...next,\n ...(action === 'hide' ? { reason: hideReason } : {}),\n revokedBy: decoded.uid,\n revokedAt: FieldValue.serverTimestamp(),\n // A restore drops takedown ownership; the field has to go, not\n // merely be overwritten, or the next restore re-deletes.\n ...(action === 'hide' ? {} : { source: FieldValue.delete() }),\n },\n { merge: true },\n )\n } else if (current) {\n await revocationRef.delete()\n }\n // Make the kill switch reach ALREADY-CACHED pages (AGL-1152). The\n // tenant stamps each plugin node with its revocation state at compose\n // time, so without this the bundle we just killed keeps executing on\n // every cached page until it happens to re-render. Best effort: the\n // tenant refuses a revoked plugin at render time regardless.\n //\n // Plugin-only: it walks the plugin INSTALL PINS, which an email\n // starter has none of, and an email is never part of a cached page —\n // its kill is felt at the send, where nothing is cached.\n if (isPlugin) await dropCachesForListing(firestore, listingId)\n // The offer follows the kill switch here too (AGL-2368). AGL-2306\n // taught the per-version revoke and the reject path to repair the\n // mirror and left this one out, so a takedown flattened `versions` to\n // `'all'` — revoking every build — while `latestApprovedVersion` went\n // on naming one. `hiddenAt` hides the listing from browse and blocks\n // the install route, so nothing was installable through it; but a\n // restore clears `hiddenAt` first and the mirror is what the update\n // badges read, and `nextRevocationState` can return a document that\n // still holds a reviewer's per-version revocations. Recomputed from\n // the versions in both directions rather than reasoned about.\n //\n // Named explicitly rather than reusing `target`: `isPlugin` is only\n // true when `reviewUid` is empty, so they are the same document — but\n // that is a fact two conditions apart, and a repair pointed at a\n // review subdocument would write a `latestApprovedVersion` onto it.\n //\n // Plugin-only, unlike the revocation write above: the mirror is\n // recomputed from `pluginVersions`, a collection no other artifact\n // type has, so running it for an email starter would derive \"nothing\n // approved\" from an empty read and write that over the listing.\n if (isPlugin) {\n await repairLatestApprovedVersion(\n firestore.collection('marketplaceListings').doc(listingId),\n next,\n )\n }\n }\n\n await addAdminAudit(firestore, {\n actorUid: decoded.uid,\n action: `plugins.takedown.${action}`,\n target: reviewUid\n ? `marketplaceListings/${listingId}/reviews/${reviewUid}`\n : `marketplaceListings/${listingId}`,\n /*\n * A review document is keyed by its AUTHOR's uid, so hiding one is a\n * staff act against a named person. The target names the review, as\n * it must — but on its own that put the takedown nowhere near the\n * page of the person whose words were removed.\n */\n ...(reviewUid ? { subjectUid: reviewUid } : {}),\n after: {\n hidden: action === 'hide',\n ...(hideReason ? { reason: hideReason } : {}),\n ...(killsInstalledCopies ? { revoked: action === 'hide' } : {}),\n // Recorded because it is a side effect of the takedown rather than\n // something the reviewer asked for, and because getting it back is a\n // re-review rather than an undo.\n ...(stripVerified ? { unverified: true } : {}),\n },\n at: FieldValue.serverTimestamp(),\n })\n\n return Response.json(\n {\n ok: true,\n hidden: action === 'hide',\n revoked: killsInstalledCopies && action === 'hide',\n // The caller shows this — a badge silently disappearing would be\n // worse than one that never existed.\n unverified: stripVerified,\n },\n { status: 200 },\n )\n }\n\n // Review checklist (AGL-963). Ticks are per {version, sha256} and carry\n // who + when, so \"verified\" is a recorded act rather than a click.\n if (action === 'checklist') {\n const version = String(body?.version ?? '')\n const itemId = String(body?.itemId ?? '')\n const checked = body?.checked !== false\n if (!listingId || !version || !REQUIRED_OR_OPTIONAL.has(itemId)) {\n return Response.json({ error: 'Unknown checklist item' }, { status: 400 })\n }\n const versionRef = firestore\n .collection('marketplaceListings')\n .doc(listingId)\n .collection('pluginVersions')\n .doc(version)\n const versionSnapshot = await versionRef.get()\n if (!versionSnapshot.exists) {\n return Response.json({ error: 'Unknown version' }, { status: 404 })\n }\n await versionRef.set(\n {\n reviewChecklist: {\n [itemId]: checked\n ? {\n by: decoded.uid,\n at: FieldValue.serverTimestamp(),\n // Pins the tick to the bytes it was made against.\n sha256: String(versionSnapshot.get('sha256') ?? ''),\n }\n : FieldValue.delete(),\n },\n },\n { merge: true },\n )\n // Audited (AGL-971): a checklist tick is the record that a human\n // looked, so it needs the same trail as the verdict it unlocks —\n // including WHICH bytes were looked at.\n await addAdminAudit(firestore, {\n actorUid: decoded.uid,\n action: `plugins.review.checklist.${checked ? 'check' : 'uncheck'}`,\n target: `marketplaceListings/${listingId}/pluginVersions/${version}`,\n after: {\n itemId,\n checked,\n sha256: String(versionSnapshot.get('sha256') ?? ''),\n },\n at: FieldValue.serverTimestamp(),\n })\n return Response.json({ ok: true, itemId, checked }, { status: 200 })\n }\n\n // Per-version verdicts (AGL-966). Approval is a statement about bytes,\n // so it lives on the version doc and installs resolve the newest\n // approved one. Listing-level list/verify still exist, but they can no\n // longer ship code past review on their own.\n if (action === 'approve-version' || action === 'reject-version') {\n const version = String(body?.version ?? '')\n const listingRef = firestore.collection('marketplaceListings').doc(listingId)\n const versionRef = listingRef.collection('pluginVersions').doc(version)\n const versionSnapshot = await versionRef.get()\n if (!versionSnapshot.exists) {\n return Response.json({ error: 'Unknown version' }, { status: 404 })\n }\n // The LISTING has to exist too, and the check belongs up here rather\n // than beside the writes that need it (AGL-1766). A version outliving\n // its listing is not hypothetical — Firestore deletes are not\n // recursive, so `pluginVersions` survives whatever removed the parent.\n //\n // The read below used to be `(await listingRef.get()).data() ?? {}`,\n // and that default was not a convenience: it is what made the two\n // mirrors fire. Both of their conditions are TRUE on `{}` —\n // `!['listed','verified'].includes('')` and `previous == null` — so an\n // absent listing was resurrected as `{ reviewStatus: 'listed' }`, i.e.\n // browsable in the marketplace, from a merge-set. (The third mirror,\n // `latestVersionReviewState`, compares `String(latestVersion ?? '')`\n // against a non-empty `version` and is false on `{}` — it could not\n // fire. The pattern is not uniform even inside one block, which is\n // exactly why AGL-1763 could not express this as a lint rule.)\n //\n // Read BEFORE the verdict write, not after it. Refusing must not\n // discard work that already happened (AGL-1760); refusing here happens\n // before anything has, so the reviewer simply retries against a\n // listing that exists. `?? {}` is gone with it — kept, it would have\n // traded a resurrection for an `update()` rejection served as a 500.\n const listingSnapshot = await listingRef.get()\n if (!listingSnapshot.exists) {\n return Response.json({ error: 'Unknown listing' }, { status: 404 })\n }\n const listing = listingSnapshot.data()\n const sha256 = String(versionSnapshot.get('sha256') ?? '')\n const approving = action === 'approve-version'\n if (approving) {\n const outstanding = outstandingChecklistItems(\n versionSnapshot.get('reviewChecklist'),\n sha256,\n )\n if (outstanding.length) {\n return Response.json(\n {\n error: `Review checklist incomplete — ${outstanding.length} required item(s) outstanding for these bytes`,\n outstanding,\n },\n { status: 409 },\n )\n }\n }\n const reason = String(body?.reason ?? '').slice(0, 500)\n // Structured rejection (AGL-977). The category is what makes rejections\n // comparable across reviewers and queryable afterwards; the comment is\n // what makes one actionable. Requiring only free text gave neither.\n const category = String(body?.category ?? '')\n if (!approving) {\n const invalid = rejectionInputError(category, reason)\n if (invalid) return Response.json({ error: invalid }, { status: 400 })\n }\n await versionRef.set(\n {\n reviewState: approving ? 'approved' : 'rejected',\n reviewedBy: decoded.uid,\n reviewedAt: FieldValue.serverTimestamp(),\n // Records WHICH bytes were approved, so a later republish under\n // the same version string can never inherit this verdict.\n reviewedSha256: sha256,\n grandfathered: FieldValue.delete(),\n ...(approving\n ? {\n // Clear a previous rejection's fields on approval, or a\n // version that was rejected and then approved keeps\n // explaining why it was turned down.\n reviewRejectionReason: FieldValue.delete(),\n reviewRejectionCategory: FieldValue.delete(),\n }\n : {\n reviewRejectionReason: reason,\n reviewRejectionCategory: category,\n // REALM TRUST IS WITHDRAWN WITH THE APPROVAL (AGL-2306).\n //\n // `admin-trust` refuses to grant `trust: 'realm'` unless the\n // version is approved — approval gates the grant. Nothing\n // ungated it: the realm load path checks `hiddenAt`, the sha,\n // the signature and the revocation, and never re-reads\n // `reviewState`. So approve → sign → re-review → reject left\n // the version loading IN THE APP REALM, where neither the\n // sandbox iframe nor the plugin-origin CSP is between it and\n // user data, until staff separately reached for the kill\n // switch.\n //\n // The same four fields `admin-trust`'s own revoke branch\n // clears, so there is one shape of \"untrusted\" rather than\n // two. This does NOT stop the bundle running as a sandboxed\n // plugin — that is what the per-version revocation beside\n // this decision is for, and it is deliberately a separate\n // choice (a thin README is the usual rejection, and an\n // unannounced site outage is worse). It withdraws only the\n // privilege that approval was the precondition for.\n trust: FieldValue.delete(),\n signature: FieldValue.delete(),\n trustGrantedBy: FieldValue.delete(),\n trustGrantedAt: FieldValue.delete(),\n }),\n },\n { merge: true },\n )\n\n // A plugin with its first approved version becomes browsable. Later\n // approvals never change listing status — that is the point: an\n // update is reviewed without disturbing what customers already have.\n //\n // The three mirrors below write through `updateExisting` as the second\n // line for the window the check above cannot close — a listing deleted\n // mid-handler. Every payload here is FLAT with no delete sentinel, so\n // the switch is semantically identical to the merge-set it replaces;\n // the `false` return needs no branch, because a listing that is gone by\n // now wants nothing mirrored onto it.\n\n // Mirror this verdict onto the listing for the marketplace (AGL-1121),\n // but ONLY when it is the newest version. Approving an old version\n // must not let the listing claim its newest bytes were reviewed —\n // which is the exact shape of the bug this mirror exists to fix.\n if (String(listing.latestVersion ?? '') === version) {\n await updateExisting(listingRef, {\n latestVersionReviewState: approving ? 'approved' : 'rejected',\n })\n }\n const listsNow =\n approving &&\n !['listed', 'verified'].includes(String(listing.reviewStatus ?? ''))\n if (listsNow) {\n await updateExisting(listingRef, {\n reviewStatus: 'listed',\n updatedAt: FieldValue.serverTimestamp(),\n })\n // Listed is browsable: the browse audience follows (AGL-3321).\n await refreshListingQueryFields(listingRef)\n }\n\n // Denormalise the newest approved version onto the listing (AGL-1016).\n // `pluginVersions` is server-only, so a console showing \"update\n // available\" has nothing else to read — and reading `latestVersion`\n // instead would advertise a version the install route refuses, leaking\n // AGL-966's guarantee back out through a badge.\n //\n // Only ever moves forward: approving an older version after a newer one\n // is a real sequence (a reviewer working through a backlog) and must not\n // walk the offer backwards.\n // What new installs receive once this lands, for the publisher's copy.\n let offeredVersion = String(listing.latestApprovedVersion ?? '')\n if (approving) {\n const previous = listing.latestApprovedVersion\n if (\n previous == null ||\n (compareArtifactVersions(String(previous), version) ?? 0) < 0\n ) {\n await updateExisting(listingRef, {\n latestApprovedVersion: version,\n updatedAt: FieldValue.serverTimestamp(),\n })\n offeredVersion = version\n }\n } else if (String(listing.latestApprovedVersion ?? '') === version) {\n // …and BACK when the version being offered is the one just rejected\n // (AGL-2306). Nothing did this, so a re-review that turned a live\n // version down left the listing advertising it — an \"Update to vX\"\n // for bytes `install-plugin` answers 409 to.\n const revocation = (\n await firestore.collection('revocations').doc(listingId).get()\n ).data() as PluginRevocation | undefined\n await repairLatestApprovedVersion(listingRef, revocation)\n }\n\n await addAdminAudit(firestore, {\n actorUid: decoded.uid,\n action: `plugins.review.version.${approving ? 'approve' : 'reject'}`,\n target: `marketplaceListings/${listingId}/pluginVersions/${version}`,\n after: {\n sha256,\n ...(reason ? { reason } : {}),\n // Both, not one (AGL-971 + AGL-977): the category is what an audit\n // can be counted by, the comment is what makes a single row make\n // sense to a human reading it later.\n ...(category ? { category } : {}),\n },\n at: FieldValue.serverTimestamp(),\n })\n\n // Pins sitting on these exact bytes right now (AGL-1085). Rejecting a\n // version does NOT stop it — the runtime resolves a pin by\n // {version, sha256} and only asks whether it is revoked — so both\n // sides have to be told, and the reviewer needs the number before\n // deciding whether to reach for the kill switch.\n const liveInstalls = Number(versionSnapshot.get('activeInstalls') ?? 0)\n const stranded = !approving && liveInstalls > 0\n\n if (listing.profileId) {\n const verdict = versionVerdictBody({\n approving,\n name: String(listing.displayName ?? ''),\n version,\n offeredVersion,\n listedNow: listsNow && !isPrivateListing(listing),\n category,\n reason,\n liveInstalls,\n })\n await notifyOrgAdmins(String(listing.profileId), {\n type: 'marketplace.review',\n level: approving ? 'success' : 'warning',\n title: approving\n ? `\"${listing.displayName}\" v${version} approved`\n : `\"${listing.displayName}\" v${version} was not approved`,\n body: verdict,\n orgId: String(listing.profileId),\n link: '/',\n }).catch(() => undefined)\n await emailPublisher(\n String(listing.profileId),\n approving\n ? `${listing.displayName} v${version} passed review`\n : `${listing.displayName} v${version} was not approved`,\n verdict,\n )\n }\n\n return Response.json(\n {\n ok: true,\n version,\n reviewState: approving ? 'approved' : 'rejected',\n // Lets the reviewer's confirmation say what it actually did, and\n // offer the kill switch when the bytes are live somewhere.\n liveInstalls,\n stranded,\n },\n { status: 200 },\n )\n }\n\n // Per-version kill switch (AGL-1085). Until now a reviewer's only way to\n // stop live bytes was a listing-wide takedown, which also revokes the\n // approved version customers are happily running and hides the listing.\n // Rejection deliberately stays a verdict rather than a kill: most\n // rejections are a thin README, and an unannounced site outage is worse\n // than the gap. This is the deliberate, adjacent action.\n //\n // This does NOT strip the Verified badge, unlike a takedown (AGL-1121).\n // The two claims are scoped differently and the split is the whole point:\n // Verified says a human vouched for the PUBLISHER and survives version\n // bumps, while the separate \"Reviewed\" chip speaks for THESE bytes and is\n // re-earned per version. Stripping the publisher claim for one bad release\n // would punish an honest mistake, and is not reversible without a full\n // re-review. A takedown is the action that contradicts the publisher claim,\n // and that one does strip it.\n //\n // That reasoning only holds because the BYTES claim is withdrawn instead —\n // which it now is, below. It was not before, and the gap is what made\n // keeping the badge unsafe rather than merely debatable.\n if (action === 'revoke-version' || action === 'unrevoke-version') {\n const version = String(body?.version ?? '')\n if (!listingId || !version) {\n return Response.json(\n { error: 'Listing and version required' },\n { status: 400 },\n )\n }\n const listingRef = firestore.collection('marketplaceListings').doc(listingId)\n const listing = (await listingRef.get()).data() ?? {}\n const revocationRef = firestore.collection('revocations').doc(listingId)\n const current = (await revocationRef.get()).data() as\n | PluginRevocation\n | undefined\n const revoking = action === 'revoke-version'\n const next = nextRevocationState(current, {\n type: revoking ? 'revoke-version' : 'unrevoke-version',\n version,\n })\n if (next) {\n await revocationRef.set(\n {\n ...next,\n reason: String(body?.reason ?? current?.reason ?? '').slice(0, 500),\n revokedBy: decoded.uid,\n revokedAt: FieldValue.serverTimestamp(),\n },\n { merge: true },\n )\n } else {\n await revocationRef.delete()\n }\n // Same reach as the takedown path above (AGL-1152): a per-version\n // revocation changes what every cached page renders, and nothing else\n // drops that HTML.\n await dropCachesForListing(firestore, listingId)\n\n // Withdraw the \"Reviewed\" chip when the bytes we just killed are the ones\n // customers are being offered (AGL-1121).\n //\n // This was ASSUMED to happen already and did not. `latestVersionReviewState`\n // is written only by approve/reject and on publish, so a version approved\n // and later revoked kept `'approved'` — and since a per-version revocation\n // deliberately does NOT hide the listing (that is the whole difference from\n // a takedown), the marketplace went on showing \"A human at Aglyn read these\n // exact bytes\" about bytes we had just stopped from executing. Revocation\n // is not surfaced anywhere else in the browse or listing UI either, so\n // nothing contradicted it.\n //\n // `revoked` rather than `rejected`: it was not turned down in review, it\n // was killed afterwards, and the audit trail should not blur those. Both\n // consumers test against `'approved'`, so this correctly drops the chip and\n // raises the caution alert.\n //\n // Only when it is the LATEST version, because that is what both consumers\n // describe. Revoking an old version says nothing about what is on offer.\n const isLatest = revocationWithdrawsReviewedClaim({\n revokedVersion: version,\n latestVersion: String(listing['latestVersion'] ?? ''),\n })\n if (isLatest) {\n const versionSnapshot = await listingRef\n .collection('pluginVersions')\n .doc(version)\n .get()\n await listingRef.set(\n {\n latestVersionReviewState: revoking\n ? REVOKED_VERSION_REVIEW_STATE\n : // Restoring returns it to whatever the version's own verdict\n // says, rather than assuming `approved` — un-revoking a version\n // that was never approved must not promote it.\n String(versionSnapshot.get('reviewState') ?? 'pending'),\n },\n { merge: true },\n )\n }\n\n // The offer follows the kill switch (AGL-2306). A revoked version is\n // refused by `install-plugin`, so leaving it as `latestApprovedVersion`\n // advertises an update that cannot be taken; un-revoking restores it.\n // Both directions, because both change what is installable.\n await repairLatestApprovedVersion(listingRef, next)\n\n await addAdminAudit(firestore, {\n actorUid: decoded.uid,\n action: `plugins.revocation.${revoking ? 'revoke' : 'restore'}`,\n target: `marketplaceListings/${listingId}/pluginVersions/${version}`,\n after: {\n versions: next?.versions ?? null,\n ...(isLatest ? { latestVersionReviewStateChanged: true } : {}),\n },\n at: FieldValue.serverTimestamp(),\n })\n\n if (listing.profileId) {\n await notifyOrgAdmins(String(listing.profileId), {\n type: 'marketplace.review',\n // Stopped on every site that runs it, which is an outage for them.\n level: revoking ? 'critical' : 'success',\n title: revoking\n ? `\"${listing.displayName}\" v${version} was stopped`\n : `\"${listing.displayName}\" v${version} was allowed to run again`,\n body: revoking\n ? `${pluginAndVersion(String(listing.displayName ?? ''), version)} ` +\n 'has been stopped on every site running it. Those sites now ' +\n 'show a placeholder where the plugin was.'\n : `${pluginAndVersion(String(listing.displayName ?? ''), version)} ` +\n 'is allowed to run again, and sites running it are no longer ' +\n 'blocked.',\n orgId: String(listing.profileId),\n link: '/',\n }).catch(() => undefined)\n }\n\n return Response.json(\n { ok: true, version, revoked: revoking },\n { status: 200 },\n )\n }\n\n // Declining a verification request (AGL-1217). Handled before the ACTIONS\n // lookup because it deliberately has NO `reviewStatus` to move to: the\n // listing stays exactly as live as it was. Refusing a badge is not a\n // verdict on the code, and routing it through the status ladder would\n // make it one.\n if (action === 'decline-verification') {\n const declineReason = String(body?.reason ?? '').trim().slice(0, 500)\n if (!listingId) {\n return Response.json({ error: 'Unknown action' }, { status: 400 })\n }\n // Required, unlike most fields here. The publisher is notified of this\n // and then has to wait out a cooldown before asking again; \"no, and we\n // will not say why, try in a month\" is not a usable answer.\n if (!declineReason) {\n return Response.json(\n { error: 'A reason is required — the publisher is told it' },\n { status: 400 },\n )\n }\n const listingRef = firestore\n .collection('marketplaceListings')\n .doc(listingId)\n const listing = (await listingRef.get()).data()\n if (!listing) {\n return Response.json({ error: 'Unknown listing' }, { status: 404 })\n }\n if (listing.verificationRequest?.state !== 'pending') {\n return Response.json(\n { error: 'No verification request is waiting' },\n { status: 409 },\n )\n }\n await listingRef.set(\n {\n verificationRequest: {\n ...listing.verificationRequest,\n state: 'declined',\n decidedAt: FieldValue.serverTimestamp(),\n decidedBy: decoded.uid,\n declineReason,\n },\n },\n { merge: true },\n )\n /*\n * THE AUDIT ROW THIS BRANCH NEVER WROTE (AGL-2328).\n *\n * Every sibling branch in this file audits, and the file header at the\n * top claims the route is audited. This one mutated\n * `verificationRequest.state`, notified the publisher and started a\n * cooldown, and left no record — so a decline was the one staff\n * decision here that could not be reviewed afterwards, on the action\n * whose reason staff are REQUIRED to type.\n *\n * `reason` uses the audit log's own reason column rather than being\n * buried in `after`, because the audit page renders and exports that\n * column and searches it (AGL-1652) — a why nobody can filter on is a\n * why nobody reads.\n */\n await addAdminAudit(firestore, {\n actorUid: decoded.uid,\n actorEmail: decoded.email ? String(decoded.email) : null,\n action: 'plugins.verification.decline',\n scope: 'marketplace',\n target: `marketplaceListings/${listingId}`,\n before: { verificationState: 'pending' },\n after: {\n verificationState: 'declined',\n cooldownDays: VERIFICATION_DECLINE_COOLDOWN_DAYS,\n },\n reason: declineReason,\n at: FieldValue.serverTimestamp(),\n })\n\n const publisherOrgId = String(listing.profileId ?? '')\n if (publisherOrgId) {\n await notifyOrgAdmins(publisherOrgId, {\n type: 'marketplace.review',\n level: 'warning',\n title: `Verification declined for \"${listing.displayName}\"`,\n // Says plainly that nothing about the listing changed. Without it a\n // publisher reads \"declined\" as a takedown of the plugin itself.\n body:\n `We declined the verified badge for ${listing.displayName}. The ` +\n 'plugin itself is unaffected and stays listed.\\n\\n' +\n `Reason: ${declineReason}\\n\\n` +\n 'You can request verification again in ' +\n `${VERIFICATION_DECLINE_COOLDOWN_DAYS} days.`,\n orgId: publisherOrgId,\n link: '/',\n }).catch(() => undefined)\n }\n return Response.json({ ok: true, state: 'declined' }, { status: 200 })\n }\n\n const nextStatus = ACTIONS[action]\n if (!listingId || !nextStatus) {\n return Response.json({ error: 'Unknown action' }, { status: 400 })\n }\n const reason = String(body?.reason ?? '').slice(0, 500)\n const category = String(body?.category ?? '')\n if (action === 'reject') {\n const invalid = rejectionInputError(category, reason)\n if (invalid) return Response.json({ error: invalid }, { status: 400 })\n }\n\n const listingRef = firestore.collection('marketplaceListings').doc(listingId)\n const listing = (await listingRef.get()).data()\n if (!listing) return Response.json({ error: 'Unknown listing' }, { status: 404 })\n\n // Both gates that expose a plugin to customers require a completed\n // checklist (AGL-963).\n //\n // `list` matters MORE than `verify`, which is easy to get backwards:\n // listing is what makes `isListingBrowsable` true, so it is the moment\n // executable third-party code becomes installable by every workspace.\n // `verify` only adds the badge on top. Gating the badge while leaving\n // listing open would put the ceremony on the weaker action.\n //\n // `start-review` and `reject` stay ungated on purpose: a reviewer must\n // be able to throw something out without first ticking eight boxes\n // about a bundle they have already decided against.\n if (action === 'verify' || action === 'list') {\n const version = String(listing.latestVersion ?? '')\n const versionSnapshot = await listingRef\n .collection('pluginVersions')\n .doc(version)\n .get()\n const outstanding = outstandingChecklistItems(\n versionSnapshot.get('reviewChecklist'),\n String(versionSnapshot.get('sha256') ?? ''),\n )\n if (outstanding.length) {\n return Response.json(\n {\n error: `Review checklist incomplete — ${outstanding.length} required item(s) outstanding`,\n outstanding,\n },\n { status: 409 },\n )\n }\n }\n\n await listingRef.set(\n {\n reviewStatus: nextStatus,\n reviewedBy: decoded.uid,\n reviewedAt: FieldValue.serverTimestamp(),\n // Granting the badge answers any request that was waiting (AGL-1217).\n // Left alone it would sit `pending` forever, so the staff queue would\n // keep offering a decision on a listing that already has the badge,\n // and the publisher's button would stay disabled as \"already pending\"\n // with nothing to wait for.\n //\n // Only when something is actually waiting: a `verify` on a listing\n // nobody asked about must not invent a request that was never made.\n ...(action === 'verify' &&\n listing.verificationRequest?.state === 'pending'\n ? {\n verificationRequest: {\n ...listing.verificationRequest,\n state: 'granted',\n decidedAt: FieldValue.serverTimestamp(),\n decidedBy: decoded.uid,\n },\n }\n : {}),\n ...(action === 'reject'\n ? { rejectionReason: reason, rejectionCategory: category }\n : {\n rejectionReason: FieldValue.delete(),\n rejectionCategory: FieldValue.delete(),\n }),\n },\n { merge: true },\n )\n // The verdict decides who browse shows the listing to (AGL-3321).\n await refreshListingQueryFields(listingRef)\n\n // Tell the publisher their listing moved (rejections especially).\n if (\n listing.profileId &&\n ['reject', 'list', 'verify', 'delist'].includes(action)\n ) {\n // Publishers are ORGS now (AGL-652), so `profileId` is an org id —\n // writing to users/{profileId}/notifications silently dropped every\n // verdict into a user document nobody reads. Notify the org's managers.\n const publisherOrgId = String(listing.profileId ?? '')\n const publisherSlug = publisherOrgId\n ? ((\n await firestore.collection('orgs').doc(publisherOrgId).get()\n ).get('slug') as string | undefined)\n : undefined\n if (publisherOrgId) {\n const isPrivate = isPrivateListing(listing)\n // One body for the feed and the email (AGL-3432): it names the\n // plugin and what happened to it, and a rejection carries its\n // category and what to fix (AGL-977).\n const verdict = listingVerdictBody({\n action,\n name: String(listing.displayName ?? ''),\n version: String(listing.latestVersion ?? ''),\n offeredVersion: String(listing.latestApprovedVersion ?? ''),\n isPrivate,\n category,\n reason,\n })\n await notifyOrgAdmins(publisherOrgId, {\n type: 'marketplace.review',\n level: action === 'reject' || action === 'delist' ? 'warning' : 'success',\n title:\n action === 'reject'\n ? `\"${listing.displayName}\" was rejected`\n : action === 'delist'\n ? `\"${listing.displayName}\" is back in review`\n : `\"${listing.displayName}\" is now ${nextStatus}`,\n body: verdict,\n orgId: publisherOrgId,\n link: publisherSlug\n ? buildRoute(Route.ORG_MARKETPLACE, {\n orgSlug: publisherSlug,\n })\n : '/',\n }).catch(() => undefined)\n await emailPublisher(\n publisherOrgId,\n action === 'reject'\n ? `${listing.displayName} was rejected — ${rejectionHeadline(\n category,\n reason,\n )}`\n : action === 'delist'\n ? isPrivate\n ? `${listing.displayName} is back in review`\n : `${listing.displayName} was removed from the marketplace`\n : `${listing.displayName} is now ${nextStatus}`,\n verdict,\n )\n }\n }\n\n await addAdminAudit(firestore, {\n actorUid: decoded.uid,\n action: `plugins.review.${action}`,\n target: `marketplaceListings/${listingId}`,\n after: { reviewStatus: nextStatus, ...(reason ? { reason } : {}) },\n at: FieldValue.serverTimestamp(),\n })\n\n return Response.json({ ok: true, reviewStatus: nextStatus }, { status: 200 })\n } catch (error) {\n // An unverifiable credential is a 401, not a fault of ours\n // (AGL-1993). Null for anything else, so a real failure keeps its 500.\n const unauthenticated = invalidIdTokenResponse(error)\n if (unauthenticated) return unauthenticated\n console.error(error)\n return Response.json({ error: 'Review action failed' }, { status: 500 })\n }\n}\n\nexport { handler as marketplaceAdminReviews }\n"],"names":["REVOKED_VERSION_REVIEW_STATE","revocationWithdrawsReviewedClaim","shouldStripVerifiedOnTakedown","VERIFIED_STRIPPED_STATUS","checkPluginBundle","isPluginRevoked","newestInstallableVersion","isStoredVerdictCurrent","nextRevocationState","PLUGIN_HOST_ABI_VERSION","PLUGIN_VERIFIER_VERSION","pluginArtifactPath","pluginRequestFromWeb","readPluginContributions","emailUnverifiedResponse","findUserByUidAcrossPools","firebaseAdmin","isImpersonationSession","updateExisting","invalidIdTokenResponse","buildRoute","Route","outstandingChecklistItems","PLUGIN_REVIEW_CHECKLIST","pluginRejectionCategory","rejectionHeadline","rejectionInputError","attestationsForBytes","VERIFICATION_DECLINE_COOLDOWN_DAYS","isPrivateListing","PUBLISHER_AGREEMENT_VERSION","publisherAgreementState","FieldValue","notifyOrgAdmins","refreshListingQueryFields","nameSearchNormalizers","planListQuery","applyListQuery","REVIEW_QUEUE_QUERY","reviewQueueBase","reviewQueueStatusClauses","emailPublisher","dropCachesForListing","addAdminAudit","compareArtifactVersions","REQUIRED_OR_OPTIONAL","Set","map","item","id","ACTIONS","list","verify","reject","delist","unverify","pluginAndVersion","name","version","rejectionReasonText","category","reason","picked","comment","trim","requiresComment","because","label","guidance","versionVerdictBody","input","offeredVersion","liveInstalls","subject","approving","offer","listedNow","filter","Boolean","join","why","listingVerdictBody","isPrivate","action","where","meanwhile","listingDetail","firestore","listingId","requestedVersion","listing","versions","attestationEntries","acceptance","listingRef","collection","doc","snapshot","get","data","Response","json","error","status","versionsSnapshot","orderBy","limit","docs","publishedAt","String","trust","sha256","hostAbi","Number","capabilities","contributes","elements","toDate","toISOString","installCount","activeInstalls","signed","repositoryUrl","reviewState","grandfathered","verification","reviewChecklist","publisherAttestation","publisherId","profileId","publisher","publisherProfile","revocation","pendingFirst","reverse","find","entry","reviewVersion","latestVersion","artifactsBucket","process","env","PLUGIN_ARTIFACTS_BUCKET","reviewEntry","reviewSha","stored","storedIsCurrent","verifier","ok","problems","checks","verifierCached","bytes","app","storage","bucket","file","download","result","toString","declaredNetwork","Array","isArray","network","origin","declaredContributions","set","verifierVersion","checkedAt","serverTimestamp","merge","catch","undefined","Object","values","attesterUid","by","attestedAt","at","attestedBy","found","record","email","displayName","description","verificationRequest","state","requestedAt","declineReason","readme","license","categories","homepageUrl","publisherName","publisherSlug","reviewStatus","rejectionReason","rejectionCategory","priceUsd","hidden","hiddenAt","hiddenReason","revoked","revokedVersions","unpublished","deletedAt","private","visibility","platformHostAbi","checklist","fromEntries","entries","checklistOutstanding","attestation","publisherAgreement","acceptedAt","required","repairLatestApprovedVersion","newest","latestApprovedVersion","delete","updatedAt","REVIEW_QUEUE_PAGE","REVIEW_QUEUE_MAX","queryParam","value","reviewQueue","query","clauses","words","split","asked","Math","floor","pageLimit","isFinite","min","listings","refused","notices","more","queue","listed","read","section","base","plan","search","length","slice","queueDocs","listedDocs","verificationDocs","Promise","all","reviewRow","latest","ref","latestReviewState","verificationRequests","versionDoc","pendingVersions","realmVersions","versionCount","publisherIds","publishers","getAll","exists","handler","request","headers","method","body","rawHeaders","authorization","idToken","startsWith","decoded","auth","verifyIdToken","email_verified","detailId","hideReason","reviewUid","target","targetSnapshot","stripVerified","isListingTarget","hiddenBy","uid","isPlugin","killsInstalledCopies","revocationRef","current","next","type","revokedBy","revokedAt","source","actorUid","subjectUid","after","unverified","versionSnapshot","itemId","checked","has","versionRef","listingSnapshot","outstanding","invalid","reviewedBy","reviewedAt","reviewedSha256","reviewRejectionReason","reviewRejectionCategory","signature","trustGrantedBy","trustGrantedAt","latestVersionReviewState","listsNow","includes","previous","stranded","verdict","level","title","orgId","link","revoking","isLatest","revokedVersion","latestVersionReviewStateChanged","decidedAt","decidedBy","actorEmail","scope","before","verificationState","cooldownDays","publisherOrgId","nextStatus","ORG_MARKETPLACE","orgSlug","unauthenticated","console","marketplaceAdminReviews"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/server/admin-reviews.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 {\n REVOKED_VERSION_REVIEW_STATE,\n revocationWithdrawsReviewedClaim,\n shouldStripVerifiedOnTakedown,\n VERIFIED_STRIPPED_STATUS,\n} from '../model/plugin-review-status'\nimport {\n checkPluginBundle,\n isPluginRevoked,\n newestInstallableVersion,\n isStoredVerdictCurrent,\n nextRevocationState,\n PLUGIN_HOST_ABI_VERSION,\n PLUGIN_VERIFIER_VERSION,\n pluginArtifactPath,\n pluginRequestFromWeb,\n readPluginContributions,\n type PluginRevocation,\n type StoredBundleVerdict,\n} from '@aglyn/aglyn/server'\nimport {\n emailUnverifiedResponse,\n findUserByUidAcrossPools,\n firebaseAdmin,\n isImpersonationSession,\n updateExisting,\n} from '@aglyn/tenant-data-admin'\nimport { invalidIdTokenResponse } from '@aglyn/tenant-data-admin/server/id-token-refusal'\nimport { buildRoute, Route } from '@aglyn/aglyn/server'\nimport {\n outstandingChecklistItems,\n PLUGIN_REVIEW_CHECKLIST,\n} from '../model/plugin-review-checklist'\nimport {\n pluginRejectionCategory,\n rejectionHeadline,\n rejectionInputError,\n} from '../model/plugin-rejection-categories'\nimport { attestationsForBytes } from '../model/publisher-attestation'\nimport { VERIFICATION_DECLINE_COOLDOWN_DAYS } from '../model/listing-verification'\nimport { isPrivateListing } from '../model/listing-visibility'\nimport {\n PUBLISHER_AGREEMENT_VERSION,\n publisherAgreementState,\n} from '../model/publisher-agreement'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport { notifyOrgAdmins } from '@aglyn/tenant-data-admin'\nimport { refreshListingQueryFields } from './listing-query-fields'\nimport { nameSearchNormalizers } from '@aglyn/aglyn/app-utils/name-search'\nimport {\n type ListQueryPlan,\n planListQuery,\n} from '@aglyn/shared-ui-jsx/const/list-query-plan'\nimport { applyListQuery } from '@aglyn/tenant-data-admin/server/list-query'\nimport {\n REVIEW_QUEUE_QUERY,\n type ReviewQueueSection,\n reviewQueueBase,\n reviewQueueStatusClauses,\n} from '../model/listing-query'\n/*\n * The publisher fan-out lives beside the usage-alert one in `_lib` rather\n * than here. Both are multi-recipient platform sends that have to read the\n * platform suppression list before addressing anybody, and that list has to\n * be reachable by a spec that does not drag the admin SDK in behind a route.\n */\nimport { emailPublisher } from './publisher-review-email'\nimport { dropCachesForListing } from './revoke-cache-drop'\nimport { addAdminAudit } from '@aglyn/tenant-data-admin/server/admin-audit-write'\nimport { compareArtifactVersions } from '../model/update-state'\n\n/**\n * Marketplace review queue (AGL-432) — Strapi Market's two-phase review\n * as staff tooling. GET returns plugin listings awaiting review\n * (submitted/in_review) with the static-verifier verdict re-run against\n * the stored artifact (the same AGL-426 checks the publish API enforced —\n * re-run here so a reviewer sees them without trusting the publish-time\n * result). POST moves a listing through the lifecycle:\n *\n * start-review → in_review list → listed (publicly browsable)\n * verify → verified (✅ badge) reject → rejected (+ reason,\n * notification to the publisher)\n *\n * Realm trust stays a SEPARATE, super-staff grant (`admin-trust.ts`) —\n * listing/verifying here never signs anything. Every action lands in\n * adminAudit.\n */\n/** Every id the checklist route will accept (AGL-963). */\nconst REQUIRED_OR_OPTIONAL = new Set(\n PLUGIN_REVIEW_CHECKLIST.map((item) => item.id),\n)\n\nconst ACTIONS: Record<string, string> = {\n 'start-review': 'in_review',\n list: 'listed',\n verify: 'verified',\n reject: 'rejected',\n // Moves BACK down the ladder (AGL-965). Review used to be a one-way\n // ratchet: the only retreats were `reject`, which reads as a verdict on\n // the whole listing and notifies the publisher as such, and takedown,\n // which is the kill switch and stops the plugin in every workspace that\n // already installed it. Neither expresses \"pull it from the marketplace\n // while we look again, without breaking the sites already using it\".\n delist: 'in_review',\n unverify: 'listed',\n}\n\n/*\n * THE WORDS A PUBLISHER READS ABOUT A VERDICT (AGL-3432).\n *\n * The publisher email (`plugin-review-update`) prints only its body, never its\n * subject, and the console feed shows the body under a short title. So every\n * body below opens by naming the plugin, the version when there is one, and\n * what happened to it. A publisher with several plugins cannot tell from\n * \"Your plugin\" which one review means.\n *\n * A private listing never reaches the marketplace (AGL-968), so its copy never\n * says it entered or left it.\n */\n\n/** \"Acme Forms version 1.2.0\", or just the name when no version is known. */\nfunction pluginAndVersion(name: string, version: string): string {\n return version ? `${name} version ${version}` : name\n}\n\n/**\n * Why it was turned down: the category and what to do about it, then the\n * reviewer's comment when there is one (AGL-977).\n *\n * A comment is required only for \"Other\", so every other rejection stands on\n * its category: printing the optional comment alone would leave most\n * rejections with a blank reason and nothing to fix. \"Other\" says nothing by\n * itself, so there the comment is the reason.\n */\nfunction rejectionReasonText(category: string, reason: string): string {\n const picked = pluginRejectionCategory(category)\n const comment = reason.trim()\n if (!picked || picked.requiresComment) return comment\n const because = `${picked.label}. ${picked.guidance}`\n return comment ? `${because}\\n\\nReviewer’s comment: ${comment}` : because\n}\n\n/** The body of a version verdict, for the console notification and the email. */\nfunction versionVerdictBody(input: {\n approving: boolean\n name: string\n version: string\n /** The version new installs receive once this verdict has landed. */\n offeredVersion: string\n /** This approval is what put the listing in the marketplace. */\n listedNow: boolean\n category: string\n reason: string\n liveInstalls: number\n}): string {\n const { name, version, offeredVersion, liveInstalls } = input\n const subject = pluginAndVersion(name, version)\n if (input.approving) {\n // Approving an OLDER version never walks the offer back, so \"now the\n // version new installs receive\" is said only when it became so.\n const offer =\n offeredVersion === version\n ? `It is now the version new installs of ${name} receive. Sites ` +\n 'already running it stay on the version they have until their ' +\n 'owners upgrade.'\n : offeredVersion\n ? `New installs still receive version ${offeredVersion}, the ` +\n 'newest approved version.'\n : ''\n return [\n `${subject} passed review.${offer ? ` ${offer}` : ''}`,\n input.listedNow ? `${name} is now listed in the marketplace.` : '',\n ]\n .filter(Boolean)\n .join('\\n\\n')\n }\n const why = rejectionReasonText(input.category, input.reason)\n return [\n `${subject} was not approved${why ? `: ${why}` : '.'}`,\n `Publishing a new version puts ${name} back in the review queue. The ` +\n 'previously approved version, if any, keeps installing in the meantime.',\n // Pins sitting on these bytes (AGL-1085). The publisher cannot uninstall\n // a version from somebody else's site, so this says what happens rather\n // than telling them to do it.\n liveInstalls > 0\n ? `Version ${version} is still running on ${liveInstalls} ` +\n `site${liveInstalls === 1 ? '' : 's'}. A rejection stops new ` +\n 'installs; it does not remove the version from sites already ' +\n 'running it.'\n : '',\n ]\n .filter(Boolean)\n .join('\\n\\n')\n}\n\n/** The body of a listing verdict, for the console notification and the email. */\nfunction listingVerdictBody(input: {\n action: string\n name: string\n /** The listing's newest bytes, the ones the reviewer read. */\n version: string\n /** The version new installs receive. */\n offeredVersion: string\n isPrivate: boolean\n category: string\n reason: string\n}): string {\n const { name, offeredVersion, isPrivate } = input\n const subject = pluginAndVersion(name, input.version)\n const comment = input.reason.trim()\n const offer = offeredVersion\n ? ` New installs receive version ${offeredVersion}.`\n : ''\n switch (input.action) {\n case 'reject': {\n // A rejection is not a delist, comment or no comment: nobody looks\n // again until the publisher fixes it and sends a new version, so it\n // never borrows the delist's \"while we review it again\".\n const why = rejectionReasonText(input.category, input.reason)\n const where = isPrivate ? 'in review' : 'from the marketplace'\n const meanwhile = isPrivate\n ? 'Sites that already installed it keep working.'\n : `${name} is out of the marketplace while it is rejected; sites ` +\n 'that already installed it keep working.'\n return [\n `${subject} was rejected ${where}${why ? `: ${why}` : '.'}`,\n `${meanwhile} To send it back for review, publish a new version.`,\n ].join('\\n\\n')\n }\n case 'delist':\n return [\n isPrivate\n ? `We moved ${subject} back into review. Sites that already ` +\n 'installed it keep working.'\n : `We removed ${subject} from the marketplace while we review it ` +\n 'again. Sites that already installed it keep working.',\n comment ? `Reviewer’s comment: ${comment}` : '',\n ]\n .filter(Boolean)\n .join('\\n\\n')\n case 'verify':\n return isPrivate\n ? `${name} is now verified.`\n : `${name} is now verified: its marketplace listing shows the ` +\n 'verified badge.'\n default:\n return isPrivate\n ? `${name} passed review. It is a private plugin, so it stays out ` +\n `of the marketplace and only your workspace can install it.${offer}`\n : `${name} passed review and is now listed in the marketplace.${offer}`\n }\n}\n\n/**\n * One listing in full, for the detail page (AGL-959). The index deals in\n * rows; everything a reviewer needs to actually decide — manifest,\n * capabilities, ABI, verifier findings, per-version trust, takedown state —\n * is assembled here rather than smeared across the list payload.\n */\nasync function listingDetail(\n firestore: FirebaseFirestore.Firestore,\n listingId: string,\n requestedVersion?: string,\n): Promise<Response> {\n const listingRef = firestore.collection('marketplaceListings').doc(listingId)\n const snapshot = await listingRef.get()\n const listing = snapshot.data()\n if (!listing) {\n return Response.json({ error: 'Unknown listing' }, { status: 404 })\n }\n\n const versionsSnapshot = await listingRef\n .collection('pluginVersions')\n .orderBy('publishedAt', 'desc')\n .limit(25)\n .get()\n const versions = versionsSnapshot.docs.map((doc) => {\n const publishedAt = doc.get('publishedAt')\n return {\n version: String(doc.get('version') ?? doc.id),\n trust: doc.get('trust') ?? null,\n sha256: String(doc.get('sha256') ?? ''),\n hostAbi: Number(doc.get('manifest.hostAbi')) || null,\n capabilities: doc.get('manifest.capabilities') ?? {},\n // What these bytes declare they contribute (AGL-3116); `null` when the\n // version predates the contract and loads under its default.\n contributes: readPluginContributions(doc.get('manifest.contributes')) ?? null,\n // Declared canvas elements (AGL-1031), for THESE bytes. Publisher copy\n // that renders in the customer's element picker and nowhere on the\n // listing page — so without it here, the `element-metadata` criterion\n // asks a reviewer to check text this page never shows (AGL-2486).\n elements: doc.get('manifest.elements') ?? [],\n publishedAt: publishedAt?.toDate?.()?.toISOString() ?? null,\n // Per-version installs (AGL-1036). \"How many workspaces would a revoke\n // actually hit\" is a per-version question, and the listing total shown\n // elsewhere on this page answers a different one.\n installCount: Number(doc.get('installCount') ?? 0),\n activeInstalls: Number(doc.get('activeInstalls') ?? 0),\n signed: Boolean(doc.get('signature')),\n // The repo as declared for THESE bytes (AGL-1076); absent on versions\n // published before it was collected.\n repositoryUrl: String(doc.get('repositoryUrl') ?? ''),\n reviewState: String(doc.get('reviewState') ?? 'pending'),\n grandfathered: Boolean(doc.get('grandfathered')),\n // Server-side only — used to decide whether the verdict below needs\n // recomputing; stripped before the payload goes out.\n verification: doc.get('verification') ?? null,\n reviewChecklist: doc.get('reviewChecklist') ?? null,\n publisherAttestation: doc.get('publisherAttestation') ?? null,\n }\n })\n\n // Publisher is an ORG since AGL-652 — show the name, not the raw id.\n const publisherId = String(listing.profileId ?? '')\n const publisher = publisherId\n ? await firestore.collection('orgs').doc(publisherId).get()\n : null\n\n // Which terms this publisher is actually under (AGL-1077). A reviewer\n // weighing a takedown, a delist, or a revocation is deciding what we are\n // entitled to do — and that is answered by the agreement the ORG accepted,\n // not by the per-version attestation beside it.\n const publisherProfile = publisherId\n ? await firestore.collection('publisherProfiles').doc(publisherId).get()\n : null\n const acceptance = publisherProfile?.get('publisherAgreement') as\n | { version?: string; acceptedBy?: string; acceptedAt?: { toDate?: () => Date } }\n | undefined\n\n // Kill switch, so the page can say whether the plugin is actually stopped\n // rather than only de-listed (AGL-948).\n const revocation = (\n await firestore.collection('revocations').doc(listingId).get()\n ).data()\n\n // The verifier verdict for the version under review (AGL-426), served\n // from the version doc when it is current (AGL-962).\n //\n // `checkPluginBundle` is pure and the artifact is immutable and\n // content-addressed, so a verdict pinned to {sha256, verifierVersion} is\n // exactly as trustworthy as re-running it — and re-running meant\n // downloading up to a megabyte from Storage on EVERY page view. A stored\n // verdict from an older checker, or for different bytes, is ignored.\n // Which version this page is reviewing (AGL-966). Defaults to the oldest\n // version still awaiting a verdict — that is the work — falling back to\n // the latest when everything is decided. A reviewer can pin any version\n // explicitly; the checklist and verifier verdict follow the selection,\n // because both are statements about specific bytes.\n const pendingFirst = [...versions]\n .reverse()\n .find((entry) => entry.reviewState !== 'approved')\n const reviewVersion =\n (requestedVersion &&\n versions.find((entry) => entry.version === requestedVersion)?.version) ||\n pendingFirst?.version ||\n String(listing.latestVersion ?? '')\n const artifactsBucket = process.env.PLUGIN_ARTIFACTS_BUCKET\n const reviewEntry = versions.find((entry) => entry.version === reviewVersion)\n const reviewSha = reviewEntry?.sha256\n const stored = reviewEntry?.verification as StoredBundleVerdict | undefined\n const storedIsCurrent = isStoredVerdictCurrent(stored, reviewSha ?? '')\n\n let verifier: unknown = storedIsCurrent\n ? {\n ok: stored?.ok,\n problems: stored?.problems ?? [],\n // The per-check summary rides along in the stored verdict (AGL-1087)\n // so the page can show what was checked without re-downloading a\n // megabyte of artifact on every view.\n checks: stored?.checks ?? [],\n }\n : null\n let verifierCached = storedIsCurrent\n if (!storedIsCurrent && artifactsBucket && reviewSha) {\n try {\n const [bytes] = await firebaseAdmin\n .app()\n .storage()\n .bucket(artifactsBucket)\n .file(pluginArtifactPath(listingId, reviewVersion, reviewSha))\n .download()\n // The manifest for THESE bytes, so an undeclared network call is a\n // finding rather than a warning (AGL-964). Read from the version doc,\n // never from the listing — capabilities change between versions.\n const result = checkPluginBundle(bytes.toString('utf8'), {\n declaredNetwork: Array.isArray(reviewEntry?.capabilities?.network)\n ? reviewEntry.capabilities.network.map((origin: unknown) =>\n String(origin),\n )\n : [],\n // Compared, never required: a version published before the contract\n // loads under its default, and review must not refuse it for a rule\n // it was never held to.\n declaredContributions: reviewEntry?.contributes ?? null,\n })\n verifier = result\n // Write it back so this sha is verified at most once platform-wide —\n // covers every listing published before the verdict was persisted at\n // publish time, and any version whose checker has since moved on.\n // Best effort: a failed write costs the next reader a re-run, nothing\n // more, so it must never fail the page.\n await listingRef\n .collection('pluginVersions')\n .doc(reviewVersion)\n .set(\n {\n verification: {\n ok: result.ok,\n problems: result.problems,\n checks: result.checks,\n sha256: reviewSha,\n verifierVersion: PLUGIN_VERIFIER_VERSION,\n checkedAt: FieldValue.serverTimestamp(),\n },\n },\n { merge: true },\n )\n .catch(() => undefined)\n } catch {\n verifier = { error: 'artifact unavailable' }\n verifierCached = false\n }\n }\n\n // Who attested, by name (AGL-969). \"A named publisher said this on a date\"\n // is the half that makes a false attestation actionable, and a raw uid\n // names nobody. Best effort — a deleted account must not blank the page.\n const attestationEntries = Object.values(\n (reviewEntry?.publisherAttestation ?? {}) as Record<\n string,\n { by?: string; at?: { toDate?: () => Date }; sha256?: string }\n >,\n ).filter((entry) => entry?.sha256 === reviewSha)\n const attesterUid = attestationEntries.find((entry) => entry.by)?.by\n const attestedAt =\n attestationEntries.find((entry) => entry.at)?.at?.toDate?.()?.toISOString() ??\n null\n let attestedBy: string | null = attesterUid ?? null\n if (attesterUid) {\n try {\n // Across pools (AGL-1122): an SSO attester is invisible to project-level\n // `getUser`, so this fell through to the catch and the review page named\n // the attester by raw uid — indistinguishable from \"account deleted\".\n const found = await findUserByUidAcrossPools(attesterUid)\n if (found) {\n attestedBy =\n found.record.email ?? found.record.displayName ?? attesterUid\n }\n } catch {\n // Account gone — the uid still identifies the statement.\n }\n }\n\n return Response.json(\n {\n listingId,\n displayName: listing.displayName ?? listingId,\n description: listing.description ?? '',\n // The publisher's ask, so the page can offer Decline beside Verify\n // (AGL-1217). Serialized: `decidedAt`/`requestedAt` are Timestamps and\n // this payload crosses JSON.\n verificationRequest: listing.verificationRequest\n ? {\n state: String(listing.verificationRequest.state ?? ''),\n requestedAt:\n listing.verificationRequest.requestedAt?.toDate?.()?.toISOString() ??\n null,\n declineReason: listing.verificationRequest.declineReason ?? null,\n }\n : null,\n readme: listing.readme ?? '',\n license: listing.license ?? '',\n categories: listing.categories ?? [],\n homepageUrl: listing.homepageUrl ?? '',\n // Prefer what the version under review declared (AGL-1076). The\n // listing carries whatever the most recent publish said, and a repo\n // can move between versions — the link a reviewer opens should be the\n // one attested against the bytes in front of them. Falls back to the\n // listing for versions published before this was collected.\n repositoryUrl: reviewEntry?.repositoryUrl || listing.repositoryUrl || '',\n publisherId,\n publisherName: publisher?.get('name') ?? publisherId,\n publisherSlug: publisher?.get('slug') ?? null,\n reviewStatus: listing.reviewStatus ?? 'submitted',\n rejectionReason: listing.rejectionReason ?? '',\n rejectionCategory: listing.rejectionCategory ?? '',\n priceUsd: Number(listing.priceUsd ?? 0),\n latestVersion: String(listing.latestVersion ?? ''),\n // The version the checklist, verifier verdict and approve/reject\n // actions on this payload refer to.\n reviewVersion,\n activeInstalls: Number(listing.activeInstalls ?? 0),\n hidden: Boolean(listing.hiddenAt),\n hiddenReason: String(listing.hiddenReason ?? ''),\n revoked: Boolean(revocation),\n revokedVersions: revocation?.versions ?? null,\n unpublished: Boolean(listing.deletedAt),\n private: listing.visibility === 'private',\n platformHostAbi: PLUGIN_HOST_ABI_VERSION,\n // Where the bundle bytes actually live (AGL-990). Staff-only payload,\n // and a bucket NAME is not a credential — reaching the object still\n // needs a Google account with access. Without it the page cannot build\n // a Cloud console link, and this bucket does not appear in the Firebase\n // console at all, so a reviewer has no way to find it by hand.\n artifactsBucket: artifactsBucket ?? null,\n versions: versions.map(\n // The rest-strip is the point: `verification`, `reviewChecklist` and\n // `publisherAttestation` are named only so they DO NOT reach the\n // client payload, which is what the disable below is for. The\n // duplicate directive that used to sit above `versions.map(` covered\n // a line with nothing unused on it, and this config treats an\n // unused disable as a failure.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n ({ verification, reviewChecklist, publisherAttestation, ...entry }) => ({\n ...entry,\n // Per-version kill-switch state (AGL-1085), read through the same\n // helper the loaders use so the page cannot disagree with the\n // runtime about whether these bytes are stopped.\n revoked: isPluginRevoked(\n revocation as PluginRevocation | undefined,\n entry.version,\n ),\n }),\n ),\n // Review checklist for the version under review (AGL-963). Ticks made\n // against different bytes do not count, so a republish starts clean.\n checklist: Object.fromEntries(\n Object.entries(\n (reviewEntry?.reviewChecklist ?? {}) as Record<\n string,\n { by?: string; sha256?: string }\n >,\n )\n .filter(([, entry]) => entry?.sha256 === reviewSha)\n .map(([id, entry]) => [id, { by: entry.by ?? null }]),\n ),\n checklistOutstanding: outstandingChecklistItems(\n reviewEntry?.reviewChecklist as never,\n reviewSha ?? '',\n ),\n // What the publisher stated about THESE bytes (AGL-969). Not a review\n // input — a reviewer still checks everything themselves — but it says\n // what was claimed, so a discrepancy is a documented false statement\n // rather than a difference of opinion. Attestations recorded against\n // other bytes are dropped, exactly like staff ticks.\n attestation: attestationsForBytes(\n reviewEntry?.publisherAttestation as never,\n reviewSha ?? '',\n ),\n attestedBy,\n attestedAt,\n // The publisher agreement this ORG is under (AGL-1077). `current` is\n // computed here rather than shipped as a boolean so the page can say\n // \"on an older version\" — which is a different thing from never\n // having accepted, and matters differently to a takedown decision.\n publisherAgreement: {\n version: acceptance?.version ?? null,\n acceptedAt: acceptance?.acceptedAt?.toDate?.()?.toISOString() ?? null,\n required: PUBLISHER_AGREEMENT_VERSION,\n state: publisherAgreementState(acceptance),\n },\n verifier,\n verifierCached,\n verifierVersion: PLUGIN_VERIFIER_VERSION,\n },\n { status: 200 },\n )\n}\n\n/**\n * Re-derive `latestApprovedVersion` from the versions themselves (AGL-2306).\n *\n * The mirror was written on approval only, with a comment saying it \"only ever\n * moves forward\" — true of approvals, and the reason it is there. What nothing\n * did was move it BACK. A version approved and later rejected on re-review, or\n * killed with the per-version switch, left the listing still advertising it:\n * `host-plugins-card` and `model/update-state` read this field to offer\n * \"Update to vX\", and `install-plugin` would then refuse those exact bytes.\n *\n * Contained, because `install-plugin` recomputes approval live and never\n * trusts this field — no unreviewed or revoked bytes could ship — but it is\n * the AGL-1016 guarantee leaking back out through a badge, which is where\n * AGL-966 came in.\n *\n * Recomputed rather than decremented: \"the newest version that is approved AND\n * not revoked\" is a property of the whole set, and stepping it back one at a\n * time is how a mirror drifts in the other direction.\n *\n * Revoked versions are excluded because the offer has to be installable, and\n * `install-plugin` 409s on a revoked pin. That also makes un-revoking restore\n * the offer, which is why this runs on both revoke and unrevoke.\n *\n * Deletes the field when nothing qualifies: an absent mirror reads as \"no\n * update to offer\", while a stale string reads as an update that does not\n * exist.\n */\nasync function repairLatestApprovedVersion(\n listingRef: FirebaseFirestore.DocumentReference,\n revocation: PluginRevocation | null | undefined,\n): Promise<void> {\n const versions = await listingRef\n .collection('pluginVersions')\n .orderBy('publishedAt', 'desc')\n .limit(50)\n .get()\n // The DECISION lives in core (`newestInstallableVersion`), where it is\n // testable without a Firestore double and shared with anything else that\n // has to answer \"what is on offer\". This function is only the query.\n const newest = newestInstallableVersion(\n versions.docs.map((doc) => ({\n version: doc.id,\n reviewState: doc.get('reviewState'),\n })),\n revocation,\n compareArtifactVersions,\n )\n await updateExisting(listingRef, {\n latestApprovedVersion: newest ?? FieldValue.delete(),\n updatedAt: FieldValue.serverTimestamp(),\n })\n}\n\n/** Rows a queue section reads before it says there are more. */\nconst REVIEW_QUEUE_PAGE = 100\n/** The most a reviewer can page the queue out to. */\nconst REVIEW_QUEUE_MAX = 1000\n\nconst queryParam = (value: unknown): string =>\n String(Array.isArray(value) ? (value[0] ?? '') : (value ?? ''))\n\n/**\n * The staff review index (AGL-961), every section on its query (AGL-3321).\n *\n * Three sections, each its own query over live plugin listings:\n *\n * - Awaiting review: the newest bytes have no approval. Read from the\n * listing's `latestVersionReviewState` summary, whose `pending` and\n * `rejected` are exactly \"the version's own `reviewState` is not\n * `approved`\" — `revoked` is the one summary value a version keeps its\n * `approved` under, and it is not in `AWAITING_REVIEW_STATES`, so a killed\n * version never re-enters the queue looking unread (AGL-1121).\n * - Listed: `reviewStatus` listed or verified.\n * - Verification requested: the publisher's standing ask (AGL-1217).\n *\n * The Status select and the search (`?status=`, `?q=`) are clauses on every\n * section's query, planned once by `REVIEW_QUEUE_QUERY`; search reads the\n * listing name's `nameTokens`. It used to be matched over one window of a\n * hundred plugin listings, so a plugin past it was in no section at all.\n * `?limit=` pages each section out; a section that holds more than it read\n * says so rather than presenting a short list as the whole queue.\n */\nasync function reviewQueue(\n firestore: FirebaseFirestore.Firestore,\n query: Record<string, unknown>,\n): Promise<Response> {\n const clauses = reviewQueueStatusClauses(queryParam(query['status']))\n const words = queryParam(query['q']).split(/\\s+/).filter(Boolean)\n const asked = Math.floor(Number(queryParam(query['limit'])))\n const pageLimit = Number.isFinite(asked) && asked > 0\n ? Math.min(asked, REVIEW_QUEUE_MAX)\n : REVIEW_QUEUE_PAGE\n const listings = firestore.collection('marketplaceListings')\n\n let refused: ListQueryPlan['refused'] = []\n let notices: string[] = []\n const more: Record<ReviewQueueSection, boolean> = {\n queue: false,\n listed: false,\n verification: false,\n }\n const read = async (section: ReviewQueueSection) => {\n const base = reviewQueueBase(section, clauses)\n // A Status the section cannot hold: empty by definition, never queried.\n if (!base) return []\n const plan = planListQuery(\n REVIEW_QUEUE_QUERY,\n { clauses, search: words, base },\n nameSearchNormalizers,\n )\n refused = plan.refused\n notices = plan.notices\n const snapshot = await applyListQuery(listings, plan)\n .limit(pageLimit + 1)\n .get()\n more[section] = snapshot.docs.length > pageLimit\n return snapshot.docs.slice(0, pageLimit)\n }\n const [queueDocs, listedDocs, verificationDocs] = await Promise.all([\n read('queue'),\n read('listed'),\n read('verification'),\n ])\n\n // Rows only (AGL-961). This used to DOWNLOAD every queued bundle to re-run\n // the verifier, so the index paid a Storage round trip per submission\n // before it could paint. The verifier now runs on the detail page, for the\n // one listing being read.\n const reviewRow = async (doc: FirebaseFirestore.QueryDocumentSnapshot) => {\n const listing = doc.data()\n const version = String(listing.latestVersion ?? '')\n const latest = version\n ? await doc.ref.collection('pluginVersions').doc(version).get()\n : null\n return {\n listingId: doc.id,\n displayName: listing.displayName ?? doc.id,\n description: listing.description ?? '',\n license: listing.license ?? '',\n categories: listing.categories ?? [],\n profileId: listing.profileId,\n reviewStatus: listing.reviewStatus ?? 'submitted',\n priceUsd: Number(listing.priceUsd ?? 0),\n version,\n hidden: Boolean(listing.hiddenAt),\n private: listing.visibility === 'private',\n latestReviewState: String(latest?.get('reviewState') ?? 'pending'),\n grandfathered: Boolean(latest?.get('grandfathered')),\n // The publisher's standing ask for the badge (AGL-1217).\n verificationRequest: listing.verificationRequest ?? null,\n }\n }\n const queue = await Promise.all(queueDocs.map(reviewRow))\n // A THIRD section, deliberately not folded into `queue` (AGL-1217).\n // `queue` means \"these bytes have not been read\"; a verification request\n // means \"this publisher asked us to vouch for who they are\". A listing can\n // sit in both at once, and merging them would hide one behind the other.\n const verificationRequests = await Promise.all(verificationDocs.map(reviewRow))\n // Listed/verified plugins with per-version trust (AGL-885): once a listing\n // leaves the queue the Grant/Revoke realm-trust actions used to leave with\n // it. This keeps every listed plugin's version trust state administrable.\n const listed = await Promise.all(\n listedDocs.map(async (doc) => {\n const listing = doc.data()\n const versionsSnapshot = await doc.ref\n .collection('pluginVersions')\n .orderBy('publishedAt', 'desc')\n .limit(10)\n .get()\n const versions = versionsSnapshot.docs.map((versionDoc) => ({\n version: String(versionDoc.get('version') ?? versionDoc.id),\n trust: versionDoc.get('trust') ?? null,\n reviewState: String(versionDoc.get('reviewState') ?? 'pending'),\n }))\n return {\n listingId: doc.id,\n displayName: listing.displayName ?? doc.id,\n reviewStatus: listing.reviewStatus,\n profileId: listing.profileId,\n latestVersion: String(listing.latestVersion ?? ''),\n // Takedown state (AGL-952), so a row shows the current verdict rather\n // than sending a reviewer into the detail page to find out whether\n // the plugin is stopped.\n hidden: Boolean(listing.hiddenAt),\n hiddenReason: String(listing.hiddenReason ?? ''),\n private: listing.visibility === 'private',\n pendingVersions: versions.filter((entry) => entry.reviewState !== 'approved')\n .length,\n // Summarised for the row; the per-version controls live on the detail\n // page (AGL-960).\n realmVersions: versions.filter((entry) => entry.trust === 'realm').length,\n versionCount: versions.length,\n versions,\n }\n }),\n )\n\n // Publisher names for scanning (AGL-961) — a listing id tells a reviewer\n // nothing. One batched read for every section.\n const publisherIds = [\n ...new Set(\n [...queue, ...listed, ...verificationRequests]\n .map((entry) => String(entry.profileId ?? ''))\n .filter(Boolean),\n ),\n ]\n const publishers: Record<string, string> = {}\n if (publisherIds.length) {\n const docs = await firestore.getAll(\n ...publisherIds.map((id) => firestore.collection('orgs').doc(id)),\n )\n for (const doc of docs) {\n if (doc.exists) publishers[doc.id] = String(doc.get('name') ?? doc.id)\n }\n }\n return Response.json(\n {\n queue,\n listed,\n publishers,\n verificationRequests,\n refused,\n notices,\n more,\n limit: pageLimit,\n },\n { status: 200 },\n )\n}\n\nasync function handler(request: Request): Promise<Response> {\n const { method, body, query, headers: rawHeaders } =\n await pluginRequestFromWeb(request)\n const headers = rawHeaders as Partial<Record<string, string>>\n const authorization = headers.authorization ?? ''\n const idToken = authorization.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length)\n : undefined\n if (!idToken) return Response.json({ error: 'Unauthenticated' }, { status: 401 })\n\n try {\n const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken)\n if (!decoded.email_verified && !isImpersonationSession(decoded)) {\n return emailUnverifiedResponse()\n }\n if (!decoded['staff']) {\n return Response.json({ error: 'Staff only' }, { status: 403 })\n }\n const firestore = firebaseAdmin.app().firestore()\n\n if (method === 'GET') {\n const detailId = String(query['listingId'] ?? '')\n if (detailId) {\n return listingDetail(firestore, detailId, String(query['version'] ?? ''))\n }\n\n return reviewQueue(firestore, query)\n }\n\n if (method !== 'POST') {\n return Response.json({ error: 'Method not allowed' }, { status: 405 })\n }\n const listingId = String(body?.listingId ?? '')\n const action = String(body?.action ?? '')\n\n // Takedown (AGL-658) is separate from the review verdict: it applies to\n // EVERY artifact type, not just plugins, and it must not overwrite a\n // plugin's `reviewStatus` — a hidden plugin that later gets un-hidden\n // should return to the verdict it had, not to square one.\n if (action === 'hide' || action === 'unhide') {\n if (!listingId) {\n return Response.json({ error: 'Missing listingId' }, { status: 400 })\n }\n const hideReason = String(body?.reason ?? '').slice(0, 500)\n if (action === 'hide' && !hideReason.trim()) {\n return Response.json({ error: 'Hiding needs a reason' }, { status: 400 })\n }\n const reviewUid = String(body?.reviewUid ?? '')\n const target = reviewUid\n ? firestore\n .collection('marketplaceListings')\n .doc(listingId)\n .collection('reviews')\n .doc(reviewUid)\n : firestore.collection('marketplaceListings').doc(listingId)\n const targetSnapshot = await target.get()\n if (!targetSnapshot.exists) {\n return Response.json({ error: 'Unknown target' }, { status: 404 })\n }\n // A takedown strips the Verified badge (AGL-1121, decided 2026-08-03).\n //\n // Verified is OUR claim that a human vouched for this publisher. Taking\n // the listing down is us saying the opposite out loud, so the two cannot\n // both stand — a badge surviving a takedown would still be asserting the\n // vouch on the listing page the moment it was restored.\n //\n // Demoted to `listed`, not to `rejected` or `submitted`: the takedown\n // itself is what makes it non-browsable (`hiddenAt`), and rewriting the\n // review verdict would destroy the record of a review that did happen.\n //\n // Restoring deliberately does NOT put the badge back. Re-granting is\n // `verify`, which re-checks the review checklist server-side; an automatic\n // regrant would hand back the badge without anyone re-forming the opinion\n // behind it. Losing it to a takedown is meant to cost a re-review.\n const stripVerified = shouldStripVerifiedOnTakedown({\n action,\n reviewStatus: targetSnapshot.get('reviewStatus'),\n isListingTarget: !reviewUid,\n })\n await target.set(\n reviewUid\n ? { hidden: action === 'hide' }\n : action === 'hide'\n ? {\n hiddenAt: FieldValue.serverTimestamp(),\n hiddenBy: decoded.uid,\n hiddenReason: hideReason,\n ...(stripVerified\n ? { reviewStatus: VERIFIED_STRIPPED_STATUS }\n : {}),\n }\n : {\n hiddenAt: FieldValue.delete(),\n hiddenBy: FieldValue.delete(),\n hiddenReason: FieldValue.delete(),\n },\n { merge: true },\n )\n // A takedown leaves browse for everyone but its publisher, and the\n // queue's Taken down status reads it (AGL-3321).\n if (!reviewUid) await refreshListingQueryFields(target)\n\n // Takedown means takedown (AGL-948). Hiding a PLUGIN listing also\n // writes the kill switch, so one staff action has one outcome\n // instead of leaving the bundle executing in every workspace that\n // already installed it. The realm join blocks on `hiddenAt` too, but\n // the revocation is what the client-side loaders and the public\n // listing-versions endpoint already understand — belt and braces on\n // the one action where a gap means running attacker code.\n const isPlugin =\n !reviewUid &&\n (targetSnapshot.get('artifactType') === 'plugin' ||\n targetSnapshot.get('type') === 'plugin')\n /**\n * An email starter earns the same treatment for the same reason.\n *\n * It is copied on install, so `hiddenAt` alone reaches only the\n * storefront: every tenant who already installed it goes on MAILING it,\n * from the sending domain every other tenant shares. The revocation is\n * what `emailStarterSendBlock` reads at the send, which is the only\n * chokepoint that can still refuse.\n *\n * The five other copied types are deliberately not here. Their content\n * is rendered on the tenant's own site, where `hiddenAt` blocking the\n * next install is the whole of what a takedown can honestly promise —\n * stopping a page from rendering is a different decision, and one nobody\n * has made.\n */\n const killsInstalledCopies =\n isPlugin ||\n (!reviewUid && targetSnapshot.get('artifactType') === 'emailStarter')\n if (killsInstalledCopies) {\n const revocationRef = firestore.collection('revocations').doc(listingId)\n const current = (await revocationRef.get()).data() as\n | PluginRevocation\n | undefined\n // Both transitions go through the shared helper (AGL-1085) so a\n // takedown and a per-version review revocation can coexist. Restoring\n // used to DELETE the doc whenever it was takedown-owned, which also\n // un-revoked any version a reviewer had stopped for an unrelated\n // reason — `reviewVersions` is what survives the round trip.\n const next = nextRevocationState(current, {\n type: action === 'hide' ? 'takedown' : 'restore',\n })\n if (next) {\n await revocationRef.set(\n {\n ...next,\n ...(action === 'hide' ? { reason: hideReason } : {}),\n revokedBy: decoded.uid,\n revokedAt: FieldValue.serverTimestamp(),\n // A restore drops takedown ownership; the field has to go, not\n // merely be overwritten, or the next restore re-deletes.\n ...(action === 'hide' ? {} : { source: FieldValue.delete() }),\n },\n { merge: true },\n )\n } else if (current) {\n await revocationRef.delete()\n }\n // Make the kill switch reach ALREADY-CACHED pages (AGL-1152). The\n // tenant stamps each plugin node with its revocation state at compose\n // time, so without this the bundle we just killed keeps executing on\n // every cached page until it happens to re-render. Best effort: the\n // tenant refuses a revoked plugin at render time regardless.\n //\n // Plugin-only: it walks the plugin INSTALL PINS, which an email\n // starter has none of, and an email is never part of a cached page —\n // its kill is felt at the send, where nothing is cached.\n if (isPlugin) await dropCachesForListing(firestore, listingId)\n // The offer follows the kill switch here too (AGL-2368). AGL-2306\n // taught the per-version revoke and the reject path to repair the\n // mirror and left this one out, so a takedown flattened `versions` to\n // `'all'` — revoking every build — while `latestApprovedVersion` went\n // on naming one. `hiddenAt` hides the listing from browse and blocks\n // the install route, so nothing was installable through it; but a\n // restore clears `hiddenAt` first and the mirror is what the update\n // badges read, and `nextRevocationState` can return a document that\n // still holds a reviewer's per-version revocations. Recomputed from\n // the versions in both directions rather than reasoned about.\n //\n // Named explicitly rather than reusing `target`: `isPlugin` is only\n // true when `reviewUid` is empty, so they are the same document — but\n // that is a fact two conditions apart, and a repair pointed at a\n // review subdocument would write a `latestApprovedVersion` onto it.\n //\n // Plugin-only, unlike the revocation write above: the mirror is\n // recomputed from `pluginVersions`, a collection no other artifact\n // type has, so running it for an email starter would derive \"nothing\n // approved\" from an empty read and write that over the listing.\n if (isPlugin) {\n await repairLatestApprovedVersion(\n firestore.collection('marketplaceListings').doc(listingId),\n next,\n )\n }\n }\n\n await addAdminAudit(firestore, {\n actorUid: decoded.uid,\n action: `plugins.takedown.${action}`,\n target: reviewUid\n ? `marketplaceListings/${listingId}/reviews/${reviewUid}`\n : `marketplaceListings/${listingId}`,\n /*\n * A review document is keyed by its AUTHOR's uid, so hiding one is a\n * staff act against a named person. The target names the review, as\n * it must — but on its own that put the takedown nowhere near the\n * page of the person whose words were removed.\n */\n ...(reviewUid ? { subjectUid: reviewUid } : {}),\n after: {\n hidden: action === 'hide',\n ...(hideReason ? { reason: hideReason } : {}),\n ...(killsInstalledCopies ? { revoked: action === 'hide' } : {}),\n // Recorded because it is a side effect of the takedown rather than\n // something the reviewer asked for, and because getting it back is a\n // re-review rather than an undo.\n ...(stripVerified ? { unverified: true } : {}),\n },\n at: FieldValue.serverTimestamp(),\n })\n\n return Response.json(\n {\n ok: true,\n hidden: action === 'hide',\n revoked: killsInstalledCopies && action === 'hide',\n // The caller shows this — a badge silently disappearing would be\n // worse than one that never existed.\n unverified: stripVerified,\n },\n { status: 200 },\n )\n }\n\n // Review checklist (AGL-963). Ticks are per {version, sha256} and carry\n // who + when, so \"verified\" is a recorded act rather than a click.\n if (action === 'checklist') {\n const version = String(body?.version ?? '')\n const itemId = String(body?.itemId ?? '')\n const checked = body?.checked !== false\n if (!listingId || !version || !REQUIRED_OR_OPTIONAL.has(itemId)) {\n return Response.json({ error: 'Unknown checklist item' }, { status: 400 })\n }\n const versionRef = firestore\n .collection('marketplaceListings')\n .doc(listingId)\n .collection('pluginVersions')\n .doc(version)\n const versionSnapshot = await versionRef.get()\n if (!versionSnapshot.exists) {\n return Response.json({ error: 'Unknown version' }, { status: 404 })\n }\n await versionRef.set(\n {\n reviewChecklist: {\n [itemId]: checked\n ? {\n by: decoded.uid,\n at: FieldValue.serverTimestamp(),\n // Pins the tick to the bytes it was made against.\n sha256: String(versionSnapshot.get('sha256') ?? ''),\n }\n : FieldValue.delete(),\n },\n },\n { merge: true },\n )\n // Audited (AGL-971): a checklist tick is the record that a human\n // looked, so it needs the same trail as the verdict it unlocks —\n // including WHICH bytes were looked at.\n await addAdminAudit(firestore, {\n actorUid: decoded.uid,\n action: `plugins.review.checklist.${checked ? 'check' : 'uncheck'}`,\n target: `marketplaceListings/${listingId}/pluginVersions/${version}`,\n after: {\n itemId,\n checked,\n sha256: String(versionSnapshot.get('sha256') ?? ''),\n },\n at: FieldValue.serverTimestamp(),\n })\n return Response.json({ ok: true, itemId, checked }, { status: 200 })\n }\n\n // Per-version verdicts (AGL-966). Approval is a statement about bytes,\n // so it lives on the version doc and installs resolve the newest\n // approved one. Listing-level list/verify still exist, but they can no\n // longer ship code past review on their own.\n if (action === 'approve-version' || action === 'reject-version') {\n const version = String(body?.version ?? '')\n const listingRef = firestore.collection('marketplaceListings').doc(listingId)\n const versionRef = listingRef.collection('pluginVersions').doc(version)\n const versionSnapshot = await versionRef.get()\n if (!versionSnapshot.exists) {\n return Response.json({ error: 'Unknown version' }, { status: 404 })\n }\n // The LISTING has to exist too, and the check belongs up here rather\n // than beside the writes that need it (AGL-1766). A version outliving\n // its listing is not hypothetical — Firestore deletes are not\n // recursive, so `pluginVersions` survives whatever removed the parent.\n //\n // The read below used to be `(await listingRef.get()).data() ?? {}`,\n // and that default was not a convenience: it is what made the two\n // mirrors fire. Both of their conditions are TRUE on `{}` —\n // `!['listed','verified'].includes('')` and `previous == null` — so an\n // absent listing was resurrected as `{ reviewStatus: 'listed' }`, i.e.\n // browsable in the marketplace, from a merge-set. (The third mirror,\n // `latestVersionReviewState`, compares `String(latestVersion ?? '')`\n // against a non-empty `version` and is false on `{}` — it could not\n // fire. The pattern is not uniform even inside one block, which is\n // exactly why AGL-1763 could not express this as a lint rule.)\n //\n // Read BEFORE the verdict write, not after it. Refusing must not\n // discard work that already happened (AGL-1760); refusing here happens\n // before anything has, so the reviewer simply retries against a\n // listing that exists. `?? {}` is gone with it — kept, it would have\n // traded a resurrection for an `update()` rejection served as a 500.\n const listingSnapshot = await listingRef.get()\n if (!listingSnapshot.exists) {\n return Response.json({ error: 'Unknown listing' }, { status: 404 })\n }\n const listing = listingSnapshot.data()\n const sha256 = String(versionSnapshot.get('sha256') ?? '')\n const approving = action === 'approve-version'\n if (approving) {\n const outstanding = outstandingChecklistItems(\n versionSnapshot.get('reviewChecklist'),\n sha256,\n )\n if (outstanding.length) {\n return Response.json(\n {\n error: `Review checklist incomplete — ${outstanding.length} required item(s) outstanding for these bytes`,\n outstanding,\n },\n { status: 409 },\n )\n }\n }\n const reason = String(body?.reason ?? '').slice(0, 500)\n // Structured rejection (AGL-977). The category is what makes rejections\n // comparable across reviewers and queryable afterwards; the comment is\n // what makes one actionable. Requiring only free text gave neither.\n const category = String(body?.category ?? '')\n if (!approving) {\n const invalid = rejectionInputError(category, reason)\n if (invalid) return Response.json({ error: invalid }, { status: 400 })\n }\n await versionRef.set(\n {\n reviewState: approving ? 'approved' : 'rejected',\n reviewedBy: decoded.uid,\n reviewedAt: FieldValue.serverTimestamp(),\n // Records WHICH bytes were approved, so a later republish under\n // the same version string can never inherit this verdict.\n reviewedSha256: sha256,\n grandfathered: FieldValue.delete(),\n ...(approving\n ? {\n // Clear a previous rejection's fields on approval, or a\n // version that was rejected and then approved keeps\n // explaining why it was turned down.\n reviewRejectionReason: FieldValue.delete(),\n reviewRejectionCategory: FieldValue.delete(),\n }\n : {\n reviewRejectionReason: reason,\n reviewRejectionCategory: category,\n // REALM TRUST IS WITHDRAWN WITH THE APPROVAL (AGL-2306).\n //\n // `admin-trust` refuses to grant `trust: 'realm'` unless the\n // version is approved — approval gates the grant. Nothing\n // ungated it: the realm load path checks `hiddenAt`, the sha,\n // the signature and the revocation, and never re-reads\n // `reviewState`. So approve → sign → re-review → reject left\n // the version loading IN THE APP REALM, where neither the\n // sandbox iframe nor the plugin-origin CSP is between it and\n // user data, until staff separately reached for the kill\n // switch.\n //\n // The same four fields `admin-trust`'s own revoke branch\n // clears, so there is one shape of \"untrusted\" rather than\n // two. This does NOT stop the bundle running as a sandboxed\n // plugin — that is what the per-version revocation beside\n // this decision is for, and it is deliberately a separate\n // choice (a thin README is the usual rejection, and an\n // unannounced site outage is worse). It withdraws only the\n // privilege that approval was the precondition for.\n trust: FieldValue.delete(),\n signature: FieldValue.delete(),\n trustGrantedBy: FieldValue.delete(),\n trustGrantedAt: FieldValue.delete(),\n }),\n },\n { merge: true },\n )\n\n // A plugin with its first approved version becomes browsable. Later\n // approvals never change listing status — that is the point: an\n // update is reviewed without disturbing what customers already have.\n //\n // The three mirrors below write through `updateExisting` as the second\n // line for the window the check above cannot close — a listing deleted\n // mid-handler. Every payload here is FLAT with no delete sentinel, so\n // the switch is semantically identical to the merge-set it replaces;\n // the `false` return needs no branch, because a listing that is gone by\n // now wants nothing mirrored onto it.\n\n // Mirror this verdict onto the listing for the marketplace (AGL-1121),\n // but ONLY when it is the newest version. Approving an old version\n // must not let the listing claim its newest bytes were reviewed —\n // which is the exact shape of the bug this mirror exists to fix.\n if (String(listing.latestVersion ?? '') === version) {\n await updateExisting(listingRef, {\n latestVersionReviewState: approving ? 'approved' : 'rejected',\n })\n }\n const listsNow =\n approving &&\n !['listed', 'verified'].includes(String(listing.reviewStatus ?? ''))\n if (listsNow) {\n await updateExisting(listingRef, {\n reviewStatus: 'listed',\n updatedAt: FieldValue.serverTimestamp(),\n })\n // Listed is browsable: the browse audience follows (AGL-3321).\n await refreshListingQueryFields(listingRef)\n }\n\n // Denormalise the newest approved version onto the listing (AGL-1016).\n // `pluginVersions` is server-only, so a console showing \"update\n // available\" has nothing else to read — and reading `latestVersion`\n // instead would advertise a version the install route refuses, leaking\n // AGL-966's guarantee back out through a badge.\n //\n // Only ever moves forward: approving an older version after a newer one\n // is a real sequence (a reviewer working through a backlog) and must not\n // walk the offer backwards.\n // What new installs receive once this lands, for the publisher's copy.\n let offeredVersion = String(listing.latestApprovedVersion ?? '')\n if (approving) {\n const previous = listing.latestApprovedVersion\n if (\n previous == null ||\n (compareArtifactVersions(String(previous), version) ?? 0) < 0\n ) {\n await updateExisting(listingRef, {\n latestApprovedVersion: version,\n updatedAt: FieldValue.serverTimestamp(),\n })\n offeredVersion = version\n }\n } else if (String(listing.latestApprovedVersion ?? '') === version) {\n // …and BACK when the version being offered is the one just rejected\n // (AGL-2306). Nothing did this, so a re-review that turned a live\n // version down left the listing advertising it — an \"Update to vX\"\n // for bytes `install-plugin` answers 409 to.\n const revocation = (\n await firestore.collection('revocations').doc(listingId).get()\n ).data() as PluginRevocation | undefined\n await repairLatestApprovedVersion(listingRef, revocation)\n }\n\n await addAdminAudit(firestore, {\n actorUid: decoded.uid,\n action: `plugins.review.version.${approving ? 'approve' : 'reject'}`,\n target: `marketplaceListings/${listingId}/pluginVersions/${version}`,\n after: {\n sha256,\n ...(reason ? { reason } : {}),\n // Both, not one (AGL-971 + AGL-977): the category is what an audit\n // can be counted by, the comment is what makes a single row make\n // sense to a human reading it later.\n ...(category ? { category } : {}),\n },\n at: FieldValue.serverTimestamp(),\n })\n\n // Pins sitting on these exact bytes right now (AGL-1085). Rejecting a\n // version does NOT stop it — the runtime resolves a pin by\n // {version, sha256} and only asks whether it is revoked — so both\n // sides have to be told, and the reviewer needs the number before\n // deciding whether to reach for the kill switch.\n const liveInstalls = Number(versionSnapshot.get('activeInstalls') ?? 0)\n const stranded = !approving && liveInstalls > 0\n\n if (listing.profileId) {\n const verdict = versionVerdictBody({\n approving,\n name: String(listing.displayName ?? ''),\n version,\n offeredVersion,\n listedNow: listsNow && !isPrivateListing(listing),\n category,\n reason,\n liveInstalls,\n })\n await notifyOrgAdmins(String(listing.profileId), {\n type: 'marketplace.review',\n level: approving ? 'success' : 'warning',\n title: approving\n ? `\"${listing.displayName}\" v${version} approved`\n : `\"${listing.displayName}\" v${version} was not approved`,\n body: verdict,\n orgId: String(listing.profileId),\n link: '/',\n }).catch(() => undefined)\n await emailPublisher(\n String(listing.profileId),\n approving\n ? `${listing.displayName} v${version} passed review`\n : `${listing.displayName} v${version} was not approved`,\n verdict,\n {\n frame: {\n plugin: String(listing.displayName ?? ''),\n version,\n outcome: approving ? 'Approved' : 'Not approved',\n },\n },\n )\n }\n\n return Response.json(\n {\n ok: true,\n version,\n reviewState: approving ? 'approved' : 'rejected',\n // Lets the reviewer's confirmation say what it actually did, and\n // offer the kill switch when the bytes are live somewhere.\n liveInstalls,\n stranded,\n },\n { status: 200 },\n )\n }\n\n // Per-version kill switch (AGL-1085). Until now a reviewer's only way to\n // stop live bytes was a listing-wide takedown, which also revokes the\n // approved version customers are happily running and hides the listing.\n // Rejection deliberately stays a verdict rather than a kill: most\n // rejections are a thin README, and an unannounced site outage is worse\n // than the gap. This is the deliberate, adjacent action.\n //\n // This does NOT strip the Verified badge, unlike a takedown (AGL-1121).\n // The two claims are scoped differently and the split is the whole point:\n // Verified says a human vouched for the PUBLISHER and survives version\n // bumps, while the separate \"Reviewed\" chip speaks for THESE bytes and is\n // re-earned per version. Stripping the publisher claim for one bad release\n // would punish an honest mistake, and is not reversible without a full\n // re-review. A takedown is the action that contradicts the publisher claim,\n // and that one does strip it.\n //\n // That reasoning only holds because the BYTES claim is withdrawn instead —\n // which it now is, below. It was not before, and the gap is what made\n // keeping the badge unsafe rather than merely debatable.\n if (action === 'revoke-version' || action === 'unrevoke-version') {\n const version = String(body?.version ?? '')\n if (!listingId || !version) {\n return Response.json(\n { error: 'Listing and version required' },\n { status: 400 },\n )\n }\n const listingRef = firestore.collection('marketplaceListings').doc(listingId)\n const listing = (await listingRef.get()).data() ?? {}\n const revocationRef = firestore.collection('revocations').doc(listingId)\n const current = (await revocationRef.get()).data() as\n | PluginRevocation\n | undefined\n const revoking = action === 'revoke-version'\n const next = nextRevocationState(current, {\n type: revoking ? 'revoke-version' : 'unrevoke-version',\n version,\n })\n if (next) {\n await revocationRef.set(\n {\n ...next,\n reason: String(body?.reason ?? current?.reason ?? '').slice(0, 500),\n revokedBy: decoded.uid,\n revokedAt: FieldValue.serverTimestamp(),\n },\n { merge: true },\n )\n } else {\n await revocationRef.delete()\n }\n // Same reach as the takedown path above (AGL-1152): a per-version\n // revocation changes what every cached page renders, and nothing else\n // drops that HTML.\n await dropCachesForListing(firestore, listingId)\n\n // Withdraw the \"Reviewed\" chip when the bytes we just killed are the ones\n // customers are being offered (AGL-1121).\n //\n // This was ASSUMED to happen already and did not. `latestVersionReviewState`\n // is written only by approve/reject and on publish, so a version approved\n // and later revoked kept `'approved'` — and since a per-version revocation\n // deliberately does NOT hide the listing (that is the whole difference from\n // a takedown), the marketplace went on showing \"A human at Aglyn read these\n // exact bytes\" about bytes we had just stopped from executing. Revocation\n // is not surfaced anywhere else in the browse or listing UI either, so\n // nothing contradicted it.\n //\n // `revoked` rather than `rejected`: it was not turned down in review, it\n // was killed afterwards, and the audit trail should not blur those. Both\n // consumers test against `'approved'`, so this correctly drops the chip and\n // raises the caution alert.\n //\n // Only when it is the LATEST version, because that is what both consumers\n // describe. Revoking an old version says nothing about what is on offer.\n const isLatest = revocationWithdrawsReviewedClaim({\n revokedVersion: version,\n latestVersion: String(listing['latestVersion'] ?? ''),\n })\n if (isLatest) {\n const versionSnapshot = await listingRef\n .collection('pluginVersions')\n .doc(version)\n .get()\n await listingRef.set(\n {\n latestVersionReviewState: revoking\n ? REVOKED_VERSION_REVIEW_STATE\n : // Restoring returns it to whatever the version's own verdict\n // says, rather than assuming `approved` — un-revoking a version\n // that was never approved must not promote it.\n String(versionSnapshot.get('reviewState') ?? 'pending'),\n },\n { merge: true },\n )\n }\n\n // The offer follows the kill switch (AGL-2306). A revoked version is\n // refused by `install-plugin`, so leaving it as `latestApprovedVersion`\n // advertises an update that cannot be taken; un-revoking restores it.\n // Both directions, because both change what is installable.\n await repairLatestApprovedVersion(listingRef, next)\n\n await addAdminAudit(firestore, {\n actorUid: decoded.uid,\n action: `plugins.revocation.${revoking ? 'revoke' : 'restore'}`,\n target: `marketplaceListings/${listingId}/pluginVersions/${version}`,\n after: {\n versions: next?.versions ?? null,\n ...(isLatest ? { latestVersionReviewStateChanged: true } : {}),\n },\n at: FieldValue.serverTimestamp(),\n })\n\n if (listing.profileId) {\n await notifyOrgAdmins(String(listing.profileId), {\n type: 'marketplace.review',\n // Stopped on every site that runs it, which is an outage for them.\n level: revoking ? 'critical' : 'success',\n title: revoking\n ? `\"${listing.displayName}\" v${version} was stopped`\n : `\"${listing.displayName}\" v${version} was allowed to run again`,\n body: revoking\n ? `${pluginAndVersion(String(listing.displayName ?? ''), version)} ` +\n 'has been stopped on every site running it. Those sites now ' +\n 'show a placeholder where the plugin was.'\n : `${pluginAndVersion(String(listing.displayName ?? ''), version)} ` +\n 'is allowed to run again, and sites running it are no longer ' +\n 'blocked.',\n orgId: String(listing.profileId),\n link: '/',\n }).catch(() => undefined)\n }\n\n return Response.json(\n { ok: true, version, revoked: revoking },\n { status: 200 },\n )\n }\n\n // Declining a verification request (AGL-1217). Handled before the ACTIONS\n // lookup because it deliberately has NO `reviewStatus` to move to: the\n // listing stays exactly as live as it was. Refusing a badge is not a\n // verdict on the code, and routing it through the status ladder would\n // make it one.\n if (action === 'decline-verification') {\n const declineReason = String(body?.reason ?? '').trim().slice(0, 500)\n if (!listingId) {\n return Response.json({ error: 'Unknown action' }, { status: 400 })\n }\n // Required, unlike most fields here. The publisher is notified of this\n // and then has to wait out a cooldown before asking again; \"no, and we\n // will not say why, try in a month\" is not a usable answer.\n if (!declineReason) {\n return Response.json(\n { error: 'A reason is required — the publisher is told it' },\n { status: 400 },\n )\n }\n const listingRef = firestore\n .collection('marketplaceListings')\n .doc(listingId)\n const listing = (await listingRef.get()).data()\n if (!listing) {\n return Response.json({ error: 'Unknown listing' }, { status: 404 })\n }\n if (listing.verificationRequest?.state !== 'pending') {\n return Response.json(\n { error: 'No verification request is waiting' },\n { status: 409 },\n )\n }\n await listingRef.set(\n {\n verificationRequest: {\n ...listing.verificationRequest,\n state: 'declined',\n decidedAt: FieldValue.serverTimestamp(),\n decidedBy: decoded.uid,\n declineReason,\n },\n },\n { merge: true },\n )\n /*\n * THE AUDIT ROW THIS BRANCH NEVER WROTE (AGL-2328).\n *\n * Every sibling branch in this file audits, and the file header at the\n * top claims the route is audited. This one mutated\n * `verificationRequest.state`, notified the publisher and started a\n * cooldown, and left no record — so a decline was the one staff\n * decision here that could not be reviewed afterwards, on the action\n * whose reason staff are REQUIRED to type.\n *\n * `reason` uses the audit log's own reason column rather than being\n * buried in `after`, because the audit page renders and exports that\n * column and searches it (AGL-1652) — a why nobody can filter on is a\n * why nobody reads.\n */\n await addAdminAudit(firestore, {\n actorUid: decoded.uid,\n actorEmail: decoded.email ? String(decoded.email) : null,\n action: 'plugins.verification.decline',\n scope: 'marketplace',\n target: `marketplaceListings/${listingId}`,\n before: { verificationState: 'pending' },\n after: {\n verificationState: 'declined',\n cooldownDays: VERIFICATION_DECLINE_COOLDOWN_DAYS,\n },\n reason: declineReason,\n at: FieldValue.serverTimestamp(),\n })\n\n const publisherOrgId = String(listing.profileId ?? '')\n if (publisherOrgId) {\n await notifyOrgAdmins(publisherOrgId, {\n type: 'marketplace.review',\n level: 'warning',\n title: `Verification declined for \"${listing.displayName}\"`,\n // Says plainly that nothing about the listing changed. Without it a\n // publisher reads \"declined\" as a takedown of the plugin itself.\n body:\n `We declined the verified badge for ${listing.displayName}. The ` +\n 'plugin itself is unaffected and stays listed.\\n\\n' +\n `Reason: ${declineReason}\\n\\n` +\n 'You can request verification again in ' +\n `${VERIFICATION_DECLINE_COOLDOWN_DAYS} days.`,\n orgId: publisherOrgId,\n link: '/',\n }).catch(() => undefined)\n }\n return Response.json({ ok: true, state: 'declined' }, { status: 200 })\n }\n\n const nextStatus = ACTIONS[action]\n if (!listingId || !nextStatus) {\n return Response.json({ error: 'Unknown action' }, { status: 400 })\n }\n const reason = String(body?.reason ?? '').slice(0, 500)\n const category = String(body?.category ?? '')\n if (action === 'reject') {\n const invalid = rejectionInputError(category, reason)\n if (invalid) return Response.json({ error: invalid }, { status: 400 })\n }\n\n const listingRef = firestore.collection('marketplaceListings').doc(listingId)\n const listing = (await listingRef.get()).data()\n if (!listing) return Response.json({ error: 'Unknown listing' }, { status: 404 })\n\n // Both gates that expose a plugin to customers require a completed\n // checklist (AGL-963).\n //\n // `list` matters MORE than `verify`, which is easy to get backwards:\n // listing is what makes `isListingBrowsable` true, so it is the moment\n // executable third-party code becomes installable by every workspace.\n // `verify` only adds the badge on top. Gating the badge while leaving\n // listing open would put the ceremony on the weaker action.\n //\n // `start-review` and `reject` stay ungated on purpose: a reviewer must\n // be able to throw something out without first ticking eight boxes\n // about a bundle they have already decided against.\n if (action === 'verify' || action === 'list') {\n const version = String(listing.latestVersion ?? '')\n const versionSnapshot = await listingRef\n .collection('pluginVersions')\n .doc(version)\n .get()\n const outstanding = outstandingChecklistItems(\n versionSnapshot.get('reviewChecklist'),\n String(versionSnapshot.get('sha256') ?? ''),\n )\n if (outstanding.length) {\n return Response.json(\n {\n error: `Review checklist incomplete — ${outstanding.length} required item(s) outstanding`,\n outstanding,\n },\n { status: 409 },\n )\n }\n }\n\n await listingRef.set(\n {\n reviewStatus: nextStatus,\n reviewedBy: decoded.uid,\n reviewedAt: FieldValue.serverTimestamp(),\n // Granting the badge answers any request that was waiting (AGL-1217).\n // Left alone it would sit `pending` forever, so the staff queue would\n // keep offering a decision on a listing that already has the badge,\n // and the publisher's button would stay disabled as \"already pending\"\n // with nothing to wait for.\n //\n // Only when something is actually waiting: a `verify` on a listing\n // nobody asked about must not invent a request that was never made.\n ...(action === 'verify' &&\n listing.verificationRequest?.state === 'pending'\n ? {\n verificationRequest: {\n ...listing.verificationRequest,\n state: 'granted',\n decidedAt: FieldValue.serverTimestamp(),\n decidedBy: decoded.uid,\n },\n }\n : {}),\n ...(action === 'reject'\n ? { rejectionReason: reason, rejectionCategory: category }\n : {\n rejectionReason: FieldValue.delete(),\n rejectionCategory: FieldValue.delete(),\n }),\n },\n { merge: true },\n )\n // The verdict decides who browse shows the listing to (AGL-3321).\n await refreshListingQueryFields(listingRef)\n\n // Tell the publisher their listing moved (rejections especially).\n if (\n listing.profileId &&\n ['reject', 'list', 'verify', 'delist'].includes(action)\n ) {\n // Publishers are ORGS now (AGL-652), so `profileId` is an org id —\n // writing to users/{profileId}/notifications silently dropped every\n // verdict into a user document nobody reads. Notify the org's managers.\n const publisherOrgId = String(listing.profileId ?? '')\n const publisherSlug = publisherOrgId\n ? ((\n await firestore.collection('orgs').doc(publisherOrgId).get()\n ).get('slug') as string | undefined)\n : undefined\n if (publisherOrgId) {\n const isPrivate = isPrivateListing(listing)\n // One body for the feed and the email (AGL-3432): it names the\n // plugin and what happened to it, and a rejection carries its\n // category and what to fix (AGL-977).\n const verdict = listingVerdictBody({\n action,\n name: String(listing.displayName ?? ''),\n version: String(listing.latestVersion ?? ''),\n offeredVersion: String(listing.latestApprovedVersion ?? ''),\n isPrivate,\n category,\n reason,\n })\n await notifyOrgAdmins(publisherOrgId, {\n type: 'marketplace.review',\n level: action === 'reject' || action === 'delist' ? 'warning' : 'success',\n title:\n action === 'reject'\n ? `\"${listing.displayName}\" was rejected`\n : action === 'delist'\n ? `\"${listing.displayName}\" is back in review`\n : `\"${listing.displayName}\" is now ${nextStatus}`,\n body: verdict,\n orgId: publisherOrgId,\n link: publisherSlug\n ? buildRoute(Route.ORG_MARKETPLACE, {\n orgSlug: publisherSlug,\n })\n : '/',\n }).catch(() => undefined)\n await emailPublisher(\n publisherOrgId,\n action === 'reject'\n ? `${listing.displayName} was rejected — ${rejectionHeadline(\n category,\n reason,\n )}`\n : action === 'delist'\n ? isPrivate\n ? `${listing.displayName} is back in review`\n : `${listing.displayName} was removed from the marketplace`\n : `${listing.displayName} is now ${nextStatus}`,\n verdict,\n {\n frame: {\n plugin: String(listing.displayName ?? ''),\n outcome:\n action === 'reject'\n ? 'Rejected'\n : action === 'delist'\n ? isPrivate\n ? 'Back in review'\n : 'Removed from the marketplace'\n : `Now ${nextStatus}`,\n },\n },\n )\n }\n }\n\n await addAdminAudit(firestore, {\n actorUid: decoded.uid,\n action: `plugins.review.${action}`,\n target: `marketplaceListings/${listingId}`,\n after: { reviewStatus: nextStatus, ...(reason ? { reason } : {}) },\n at: FieldValue.serverTimestamp(),\n })\n\n return Response.json({ ok: true, reviewStatus: nextStatus }, { status: 200 })\n } catch (error) {\n // An unverifiable credential is a 401, not a fault of ours\n // (AGL-1993). Null for anything else, so a real failure keeps its 500.\n const unauthenticated = invalidIdTokenResponse(error)\n if (unauthenticated) return unauthenticated\n console.error(error)\n return Response.json({ error: 'Review action failed' }, { status: 500 })\n }\n}\n\nexport { handler as marketplaceAdminReviews }\n"],"names":["REVOKED_VERSION_REVIEW_STATE","revocationWithdrawsReviewedClaim","shouldStripVerifiedOnTakedown","VERIFIED_STRIPPED_STATUS","checkPluginBundle","isPluginRevoked","newestInstallableVersion","isStoredVerdictCurrent","nextRevocationState","PLUGIN_HOST_ABI_VERSION","PLUGIN_VERIFIER_VERSION","pluginArtifactPath","pluginRequestFromWeb","readPluginContributions","emailUnverifiedResponse","findUserByUidAcrossPools","firebaseAdmin","isImpersonationSession","updateExisting","invalidIdTokenResponse","buildRoute","Route","outstandingChecklistItems","PLUGIN_REVIEW_CHECKLIST","pluginRejectionCategory","rejectionHeadline","rejectionInputError","attestationsForBytes","VERIFICATION_DECLINE_COOLDOWN_DAYS","isPrivateListing","PUBLISHER_AGREEMENT_VERSION","publisherAgreementState","FieldValue","notifyOrgAdmins","refreshListingQueryFields","nameSearchNormalizers","planListQuery","applyListQuery","REVIEW_QUEUE_QUERY","reviewQueueBase","reviewQueueStatusClauses","emailPublisher","dropCachesForListing","addAdminAudit","compareArtifactVersions","REQUIRED_OR_OPTIONAL","Set","map","item","id","ACTIONS","list","verify","reject","delist","unverify","pluginAndVersion","name","version","rejectionReasonText","category","reason","picked","comment","trim","requiresComment","because","label","guidance","versionVerdictBody","input","offeredVersion","liveInstalls","subject","approving","offer","listedNow","filter","Boolean","join","why","listingVerdictBody","isPrivate","action","where","meanwhile","listingDetail","firestore","listingId","requestedVersion","listing","versions","attestationEntries","acceptance","listingRef","collection","doc","snapshot","get","data","Response","json","error","status","versionsSnapshot","orderBy","limit","docs","publishedAt","String","trust","sha256","hostAbi","Number","capabilities","contributes","elements","toDate","toISOString","installCount","activeInstalls","signed","repositoryUrl","reviewState","grandfathered","verification","reviewChecklist","publisherAttestation","publisherId","profileId","publisher","publisherProfile","revocation","pendingFirst","reverse","find","entry","reviewVersion","latestVersion","artifactsBucket","process","env","PLUGIN_ARTIFACTS_BUCKET","reviewEntry","reviewSha","stored","storedIsCurrent","verifier","ok","problems","checks","verifierCached","bytes","app","storage","bucket","file","download","result","toString","declaredNetwork","Array","isArray","network","origin","declaredContributions","set","verifierVersion","checkedAt","serverTimestamp","merge","catch","undefined","Object","values","attesterUid","by","attestedAt","at","attestedBy","found","record","email","displayName","description","verificationRequest","state","requestedAt","declineReason","readme","license","categories","homepageUrl","publisherName","publisherSlug","reviewStatus","rejectionReason","rejectionCategory","priceUsd","hidden","hiddenAt","hiddenReason","revoked","revokedVersions","unpublished","deletedAt","private","visibility","platformHostAbi","checklist","fromEntries","entries","checklistOutstanding","attestation","publisherAgreement","acceptedAt","required","repairLatestApprovedVersion","newest","latestApprovedVersion","delete","updatedAt","REVIEW_QUEUE_PAGE","REVIEW_QUEUE_MAX","queryParam","value","reviewQueue","query","clauses","words","split","asked","Math","floor","pageLimit","isFinite","min","listings","refused","notices","more","queue","listed","read","section","base","plan","search","length","slice","queueDocs","listedDocs","verificationDocs","Promise","all","reviewRow","latest","ref","latestReviewState","verificationRequests","versionDoc","pendingVersions","realmVersions","versionCount","publisherIds","publishers","getAll","exists","handler","request","headers","method","body","rawHeaders","authorization","idToken","startsWith","decoded","auth","verifyIdToken","email_verified","detailId","hideReason","reviewUid","target","targetSnapshot","stripVerified","isListingTarget","hiddenBy","uid","isPlugin","killsInstalledCopies","revocationRef","current","next","type","revokedBy","revokedAt","source","actorUid","subjectUid","after","unverified","versionSnapshot","itemId","checked","has","versionRef","listingSnapshot","outstanding","invalid","reviewedBy","reviewedAt","reviewedSha256","reviewRejectionReason","reviewRejectionCategory","signature","trustGrantedBy","trustGrantedAt","latestVersionReviewState","listsNow","includes","previous","stranded","verdict","level","title","orgId","link","frame","plugin","outcome","revoking","isLatest","revokedVersion","latestVersionReviewStateChanged","decidedAt","decidedBy","actorEmail","scope","before","verificationState","cooldownDays","publisherOrgId","nextStatus","ORG_MARKETPLACE","orgSlug","unauthenticated","console","marketplaceAdminReviews"],"mappings":"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"}
|
|
@@ -48,7 +48,15 @@
|
|
|
48
48
|
* verdict of a busy week.
|
|
49
49
|
*/
|
|
50
50
|
export declare function emailPublisher(orgId: string, subject: string, text: string,
|
|
51
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* `firestore` is injectable for tests; the list read defaults to the admin
|
|
53
|
+
* Firestore. `frame` is the verdict as the designed email's details panel.
|
|
54
|
+
*/
|
|
52
55
|
options?: {
|
|
53
56
|
firestore?: any;
|
|
57
|
+
frame?: {
|
|
58
|
+
plugin: string;
|
|
59
|
+
version?: string;
|
|
60
|
+
outcome: string;
|
|
61
|
+
};
|
|
54
62
|
}): Promise<void>;
|
|
@@ -25,6 +25,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
25
25
|
*/ import { filterSuppressedEmails } from "@aglyn/tenant-data-admin/server/email-suppression";
|
|
26
26
|
import { renderSystemEmailContent, systemEmailBrand } from "@aglyn/tenant-data-admin/server/render-system-email";
|
|
27
27
|
import { sendEmail } from "@aglyn/shared-util-email";
|
|
28
|
+
import { platformConsoleOrigin } from "@aglyn/aglyn/app-utils/platform-brand";
|
|
28
29
|
/**
|
|
29
30
|
* Emails a publisher's owners and admins about a review outcome (AGL-972).
|
|
30
31
|
*
|
|
@@ -57,7 +58,10 @@ import { sendEmail } from "@aglyn/shared-util-email";
|
|
|
57
58
|
* newsletter — and the gate would apply a topic the publisher never
|
|
58
59
|
* subscribed to and a frequency cap that would silently drop the second
|
|
59
60
|
* verdict of a busy week.
|
|
60
|
-
*/ export async function emailPublisher(orgId, subject, text, /**
|
|
61
|
+
*/ export async function emailPublisher(orgId, subject, text, /**
|
|
62
|
+
* `firestore` is injectable for tests; the list read defaults to the admin
|
|
63
|
+
* Firestore. `frame` is the verdict as the designed email's details panel.
|
|
64
|
+
*/ options) {
|
|
61
65
|
try {
|
|
62
66
|
const members = await listOrgMembers(orgId);
|
|
63
67
|
const uids = members.filter((member)=>member.role === 'owner' || member.role === 'admin').map((member)=>member.$id);
|
|
@@ -72,9 +76,25 @@ import { sendEmail } from "@aglyn/shared-util-email";
|
|
|
72
76
|
if (!recipients.length) return;
|
|
73
77
|
// The `plugin-review-update` system email (AGL-3367), in the platform's
|
|
74
78
|
// brand: review is the platform talking about its own marketplace.
|
|
79
|
+
// The publisher's Marketplace page, where the listing and its versions are.
|
|
80
|
+
let slug = '';
|
|
81
|
+
try {
|
|
82
|
+
var _ref, _org_get;
|
|
83
|
+
const org = await ((_ref = options == null ? void 0 : options.firestore) != null ? _ref : firebaseAdmin.app().firestore()).collection('orgs').doc(orgId).get();
|
|
84
|
+
slug = String((_org_get = org.get('slug')) != null ? _org_get : '').trim();
|
|
85
|
+
} catch (unused) {
|
|
86
|
+
// Unread, the button opens the console home instead.
|
|
87
|
+
}
|
|
88
|
+
const frame = options == null ? void 0 : options.frame;
|
|
75
89
|
const content = await renderSystemEmailContent('plugin-review-update', {
|
|
76
90
|
'review.subject': subject,
|
|
77
|
-
'review.body': text
|
|
91
|
+
'review.body': text,
|
|
92
|
+
'review.details': frame ? [
|
|
93
|
+
`Plugin: ${frame.plugin}`,
|
|
94
|
+
frame.version ? `Version: ${frame.version}` : '',
|
|
95
|
+
`Outcome: ${frame.outcome}`
|
|
96
|
+
].filter(Boolean).join('\n') : '',
|
|
97
|
+
'review.url': `${platformConsoleOrigin()}${slug ? `/${slug}/marketplace` : ''}`
|
|
78
98
|
}, systemEmailBrand(null), {
|
|
79
99
|
subject,
|
|
80
100
|
text
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/server/publisher-review-email.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 {\n firebaseAdmin,\n listOrgMembers,\n meterPlatformEmail,\n} from '@aglyn/tenant-data-admin'\n/*\n * From the LEAF, not the barrel (AGL-2407), for the reason\n * `usage-alert-email.ts` states: the review route's own spec mocks\n * `@aglyn/tenant-data-admin` wholesale because its graph reaches the admin\n * SDK, and a `jest.mock` factory is a closed world. A gate imported through\n * the barrel is silently replaced by `undefined` or by whatever the factory\n * happens to list, which is a check that reads as present and runs never.\n */\nimport { filterSuppressedEmails } from '@aglyn/tenant-data-admin/server/email-suppression'\nimport {\n renderSystemEmailContent,\n systemEmailBrand,\n} from '@aglyn/tenant-data-admin/server/render-system-email'\nimport { sendEmail } from '@aglyn/shared-util-email'\n\n/**\n * Emails a publisher's owners and admins about a review outcome (AGL-972).\n *\n * In-app notifications alone assume the publisher is sitting in the console\n * — but review is asynchronous by nature: a submission can wait days, and\n * the publisher has no reason to keep checking. Best effort, and never\n * allowed to fail the verdict that triggered it.\n *\n * ## The platform hard-bounce list, and deliberately NOT the marketing gate\n *\n * This is the console's one multi-recipient platform fan-out, and a fan-out\n * is the shape the suppression list exists for: a publisher who submits\n * monthly is mailed monthly, and an address that permanently bounced is\n * re-attempted on every submission forever unless something looks. Repeat\n * delivery to a mailbox that has said it does not exist is what a mailbox\n * provider scores a sending domain on, and `aglyn.com` carries the password\n * resets and receipts on the same key.\n *\n * So it reads {@link filterSuppressedEmails} — the PLATFORM list, which\n * answers \"did this mailbox permanently fail, or did its owner report spam,\n * anywhere in the product\". It does not read `filterSendableForHost`: that\n * one adds `hosts/{hostId}/suppressions`, whose entries mean \"not from this\n * SITE'S campaigns\", and a review verdict is sent on no site's behalf. There\n * is no host to key it by, and a publisher opting out of their own site's\n * marketing says nothing about whether they want to hear that their plugin\n * was rejected.\n *\n * It also does not pass the marketing gate. A review verdict is a platform\n * notice to a publisher about their own submission — nearer a receipt than a\n * newsletter — and the gate would apply a topic the publisher never\n * subscribed to and a frequency cap that would silently drop the second\n * verdict of a busy week.\n */\nexport async function emailPublisher(\n orgId: string,\n subject: string,\n text: string,\n
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/server/publisher-review-email.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 {\n firebaseAdmin,\n listOrgMembers,\n meterPlatformEmail,\n} from '@aglyn/tenant-data-admin'\n/*\n * From the LEAF, not the barrel (AGL-2407), for the reason\n * `usage-alert-email.ts` states: the review route's own spec mocks\n * `@aglyn/tenant-data-admin` wholesale because its graph reaches the admin\n * SDK, and a `jest.mock` factory is a closed world. A gate imported through\n * the barrel is silently replaced by `undefined` or by whatever the factory\n * happens to list, which is a check that reads as present and runs never.\n */\nimport { filterSuppressedEmails } from '@aglyn/tenant-data-admin/server/email-suppression'\nimport {\n renderSystemEmailContent,\n systemEmailBrand,\n} from '@aglyn/tenant-data-admin/server/render-system-email'\nimport { sendEmail } from '@aglyn/shared-util-email'\nimport { platformConsoleOrigin } from '@aglyn/aglyn/app-utils/platform-brand'\n\n/**\n * Emails a publisher's owners and admins about a review outcome (AGL-972).\n *\n * In-app notifications alone assume the publisher is sitting in the console\n * — but review is asynchronous by nature: a submission can wait days, and\n * the publisher has no reason to keep checking. Best effort, and never\n * allowed to fail the verdict that triggered it.\n *\n * ## The platform hard-bounce list, and deliberately NOT the marketing gate\n *\n * This is the console's one multi-recipient platform fan-out, and a fan-out\n * is the shape the suppression list exists for: a publisher who submits\n * monthly is mailed monthly, and an address that permanently bounced is\n * re-attempted on every submission forever unless something looks. Repeat\n * delivery to a mailbox that has said it does not exist is what a mailbox\n * provider scores a sending domain on, and `aglyn.com` carries the password\n * resets and receipts on the same key.\n *\n * So it reads {@link filterSuppressedEmails} — the PLATFORM list, which\n * answers \"did this mailbox permanently fail, or did its owner report spam,\n * anywhere in the product\". It does not read `filterSendableForHost`: that\n * one adds `hosts/{hostId}/suppressions`, whose entries mean \"not from this\n * SITE'S campaigns\", and a review verdict is sent on no site's behalf. There\n * is no host to key it by, and a publisher opting out of their own site's\n * marketing says nothing about whether they want to hear that their plugin\n * was rejected.\n *\n * It also does not pass the marketing gate. A review verdict is a platform\n * notice to a publisher about their own submission — nearer a receipt than a\n * newsletter — and the gate would apply a topic the publisher never\n * subscribed to and a frequency cap that would silently drop the second\n * verdict of a busy week.\n */\nexport async function emailPublisher(\n orgId: string,\n subject: string,\n text: string,\n /**\n * `firestore` is injectable for tests; the list read defaults to the admin\n * Firestore. `frame` is the verdict as the designed email's details panel.\n */\n options?: {\n firestore?: any\n frame?: { plugin: string; version?: string; outcome: string }\n },\n): Promise<void> {\n try {\n const members = await listOrgMembers(orgId)\n const uids = members\n .filter((member) => member.role === 'owner' || member.role === 'admin')\n .map((member) => member.$id)\n if (!uids.length) return\n const users = await firebaseAdmin\n .app()\n .auth()\n .getUsers(uids.map((uid) => ({ uid })))\n const addresses = users.users\n .map((user) => user.email)\n .filter((email): email is string => Boolean(email))\n // Per address, so one dead mailbox among a publisher's admins does not\n // silence the verdict for the rest of them.\n const recipients = await filterSuppressedEmails(\n addresses,\n options?.firestore,\n )\n if (!recipients.length) return\n // The `plugin-review-update` system email (AGL-3367), in the platform's\n // brand: review is the platform talking about its own marketplace.\n // The publisher's Marketplace page, where the listing and its versions are.\n let slug = ''\n try {\n const org = await (options?.firestore ?? firebaseAdmin.app().firestore())\n .collection('orgs')\n .doc(orgId)\n .get()\n slug = String(org.get('slug') ?? '').trim()\n } catch {\n // Unread, the button opens the console home instead.\n }\n const frame = options?.frame\n const content = await renderSystemEmailContent(\n 'plugin-review-update',\n {\n 'review.subject': subject,\n 'review.body': text,\n 'review.details': frame\n ? [\n `Plugin: ${frame.plugin}`,\n frame.version ? `Version: ${frame.version}` : '',\n `Outcome: ${frame.outcome}`,\n ]\n .filter(Boolean)\n .join('\\n')\n : '',\n 'review.url': `${platformConsoleOrigin()}${slug ? `/${slug}/marketplace` : ''}`,\n },\n systemEmailBrand(null),\n { subject, text },\n )\n const results = await Promise.all(\n recipients.map((to) =>\n sendEmail({ to, ...content, context: 'plugin review update' }),\n ),\n )\n // Cost meter (AGL-1438). Platform-scoped: marketplace review is Aglyn's\n // own workflow talking to a publisher, not mail the publisher's org sent.\n await meterPlatformEmail(results.filter((result) => result.sent).length)\n } catch (error) {\n console.error('publisher review email failed', error)\n }\n}\n"],"names":["firebaseAdmin","listOrgMembers","meterPlatformEmail","filterSuppressedEmails","renderSystemEmailContent","systemEmailBrand","sendEmail","platformConsoleOrigin","emailPublisher","orgId","subject","text","options","members","uids","filter","member","role","map","$id","length","users","app","auth","getUsers","uid","addresses","user","email","Boolean","recipients","firestore","slug","org","collection","doc","get","String","trim","frame","content","plugin","version","outcome","join","results","Promise","all","to","context","result","sent","error","console"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,aAAa,EACbC,cAAc,EACdC,kBAAkB,QACb,2BAA0B;AACjC;;;;;;;CAOC,GACD,SAASC,sBAAsB,QAAQ,oDAAmD;AAC1F,SACEC,wBAAwB,EACxBC,gBAAgB,QACX,sDAAqD;AAC5D,SAASC,SAAS,QAAQ,2BAA0B;AACpD,SAASC,qBAAqB,QAAQ,wCAAuC;AAE7E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgCC,GACD,OAAO,eAAeC,eACpBC,KAAa,EACbC,OAAe,EACfC,IAAY,EACZ;;;GAGC,GACDC,OAGC;IAED,IAAI;QACF,MAAMC,UAAU,MAAMZ,eAAeQ;QACrC,MAAMK,OAAOD,QACVE,MAAM,CAAC,CAACC,SAAWA,OAAOC,IAAI,KAAK,WAAWD,OAAOC,IAAI,KAAK,SAC9DC,GAAG,CAAC,CAACF,SAAWA,OAAOG,GAAG;QAC7B,IAAI,CAACL,KAAKM,MAAM,EAAE;QAClB,MAAMC,QAAQ,MAAMrB,cACjBsB,GAAG,GACHC,IAAI,GACJC,QAAQ,CAACV,KAAKI,GAAG,CAAC,CAACO,MAAS,CAAA;gBAAEA;YAAI,CAAA;QACrC,MAAMC,YAAYL,MAAMA,KAAK,CAC1BH,GAAG,CAAC,CAACS,OAASA,KAAKC,KAAK,EACxBb,MAAM,CAAC,CAACa,QAA2BC,QAAQD;QAC9C,uEAAuE;QACvE,4CAA4C;QAC5C,MAAME,aAAa,MAAM3B,uBACvBuB,WACAd,2BAAAA,QAASmB,SAAS;QAEpB,IAAI,CAACD,WAAWV,MAAM,EAAE;QACxB,wEAAwE;QACxE,mEAAmE;QACnE,4EAA4E;QAC5E,IAAIY,OAAO;QACX,IAAI;sBAKYC;YAJd,MAAMA,MAAM,MAAM,SAACrB,2BAAAA,QAASmB,SAAS,mBAAI/B,cAAcsB,GAAG,GAAGS,SAAS,IACnEG,UAAU,CAAC,QACXC,GAAG,CAAC1B,OACJ2B,GAAG;YACNJ,OAAOK,QAAOJ,WAAAA,IAAIG,GAAG,CAAC,mBAARH,WAAmB,IAAIK,IAAI;QAC3C,EAAE,eAAM;QACN,qDAAqD;QACvD;QACA,MAAMC,QAAQ3B,2BAAAA,QAAS2B,KAAK;QAC5B,MAAMC,UAAU,MAAMpC,yBACpB,wBACA;YACE,kBAAkBM;YAClB,eAAeC;YACf,kBAAkB4B,QACd;gBACE,CAAC,QAAQ,EAAEA,MAAME,MAAM,EAAE;gBACzBF,MAAMG,OAAO,GAAG,CAAC,SAAS,EAAEH,MAAMG,OAAO,EAAE,GAAG;gBAC9C,CAAC,SAAS,EAAEH,MAAMI,OAAO,EAAE;aAC5B,CACE5B,MAAM,CAACc,SACPe,IAAI,CAAC,QACR;YACJ,cAAc,GAAGrC,0BAA0ByB,OAAO,CAAC,CAAC,EAAEA,KAAK,YAAY,CAAC,GAAG,IAAI;QACjF,GACA3B,iBAAiB,OACjB;YAAEK;YAASC;QAAK;QAElB,MAAMkC,UAAU,MAAMC,QAAQC,GAAG,CAC/BjB,WAAWZ,GAAG,CAAC,CAAC8B,KACd1C,UAAU;gBAAE0C;eAAOR;gBAASS,SAAS;;QAGzC,wEAAwE;QACxE,0EAA0E;QAC1E,MAAM/C,mBAAmB2C,QAAQ9B,MAAM,CAAC,CAACmC,SAAWA,OAAOC,IAAI,EAAE/B,MAAM;IACzE,EAAE,OAAOgC,OAAO;QACdC,QAAQD,KAAK,CAAC,iCAAiCA;IACjD;AACF"}
|