@aglyn/plugins-marketplace 1.0.0-beta.218 → 1.0.0-beta.219

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.
Files changed (128) hide show
  1. package/README.md +9 -4
  2. package/package.json +14 -14
  3. package/src/index.d.ts +1 -0
  4. package/src/index.js.map +1 -1
  5. package/src/lib/components/host-plugins-card.component.js +4 -3
  6. package/src/lib/components/host-plugins-card.component.js.map +1 -1
  7. package/src/lib/components/listing-content.component.js +2 -1
  8. package/src/lib/components/listing-content.component.js.map +1 -1
  9. package/src/lib/components/marketplace-payments-notice.component.d.ts +6 -0
  10. package/src/lib/components/marketplace-payments-notice.component.js +9 -2
  11. package/src/lib/components/marketplace-payments-notice.component.js.map +1 -1
  12. package/src/lib/components/org-plugin-installs-card.component.d.ts +20 -0
  13. package/src/lib/components/org-plugin-installs-card.component.js +254 -0
  14. package/src/lib/components/org-plugin-installs-card.component.js.map +1 -0
  15. package/src/lib/components/org-publish-panel.component.js +1 -1
  16. package/src/lib/components/org-publish-panel.component.js.map +1 -1
  17. package/src/lib/components/org-seller-panel.component.js +1 -1
  18. package/src/lib/components/org-seller-panel.component.js.map +1 -1
  19. package/src/lib/components/plugin-install-status.component.d.ts +13 -0
  20. package/src/lib/components/plugin-install-status.component.js +85 -0
  21. package/src/lib/components/plugin-install-status.component.js.map +1 -0
  22. package/src/lib/components/plugin-site-set.component.js +1 -1
  23. package/src/lib/components/plugin-site-set.component.js.map +1 -1
  24. package/src/lib/components/publisher-agreement-client.js +1 -1
  25. package/src/lib/components/publisher-agreement-client.js.map +1 -1
  26. package/src/lib/components/publisher-agreement-dialog.component.js +1 -1
  27. package/src/lib/components/publisher-agreement-dialog.component.js.map +1 -1
  28. package/src/lib/components/publisher-agreement-summary.component.js +1 -1
  29. package/src/lib/components/publisher-agreement-summary.component.js.map +1 -1
  30. package/src/lib/components/staff-marketplace-overview.component.d.ts +14 -0
  31. package/src/lib/components/staff-marketplace-overview.component.js +116 -0
  32. package/src/lib/components/staff-marketplace-overview.component.js.map +1 -0
  33. package/src/lib/components/staff-reversal-recovery-card.component.d.ts +51 -0
  34. package/src/lib/components/staff-reversal-recovery-card.component.js +97 -0
  35. package/src/lib/components/staff-reversal-recovery-card.component.js.map +1 -0
  36. package/src/lib/components/template-gallery-shelf.component.d.ts +22 -0
  37. package/src/lib/components/template-gallery-shelf.component.js +267 -0
  38. package/src/lib/components/template-gallery-shelf.component.js.map +1 -0
  39. package/src/lib/components/template-install-status.component.d.ts +19 -0
  40. package/src/lib/components/template-install-status.component.js +153 -0
  41. package/src/lib/components/template-install-status.component.js.map +1 -0
  42. package/src/lib/components/use-publisher-agreement-gate.js +1 -1
  43. package/src/lib/components/use-publisher-agreement-gate.js.map +1 -1
  44. package/src/lib/constants/template-source.d.ts +24 -0
  45. package/src/lib/constants/template-source.js +24 -0
  46. package/src/lib/constants/template-source.js.map +1 -0
  47. package/src/lib/declarations.server.d.ts +30 -0
  48. package/src/lib/declarations.server.js +53 -0
  49. package/src/lib/declarations.server.js.map +1 -0
  50. package/src/lib/model/artifact-merge.js +1 -1
  51. package/src/lib/model/artifact-merge.js.map +1 -1
  52. package/src/lib/model/index.d.ts +2 -2
  53. package/src/lib/model/index.js +2 -2
  54. package/src/lib/model/index.js.map +1 -1
  55. package/src/lib/model/listing-query.js +1 -1
  56. package/src/lib/model/listing-query.js.map +1 -1
  57. package/src/lib/model/listing-social-card.js +1 -1
  58. package/src/lib/model/listing-social-card.js.map +1 -1
  59. package/src/lib/model/listing-verification.d.ts +1 -1
  60. package/src/lib/model/listing-verification.js +1 -1
  61. package/src/lib/model/listing-verification.js.map +1 -1
  62. package/src/lib/model/listing-visibility.d.ts +118 -0
  63. package/src/lib/model/listing-visibility.js +143 -0
  64. package/src/lib/model/listing-visibility.js.map +1 -0
  65. package/src/lib/model/marketplace.d.ts +15 -13
  66. package/src/lib/model/marketplace.js +12 -10
  67. package/src/lib/model/marketplace.js.map +1 -1
  68. package/src/lib/model/publisher-agreement-refusal.d.ts +1 -1
  69. package/src/lib/model/publisher-agreement-refusal.js.map +1 -1
  70. package/src/lib/model/publisher-agreement.d.ts +289 -0
  71. package/src/lib/model/publisher-agreement.js +321 -0
  72. package/src/lib/model/publisher-agreement.js.map +1 -0
  73. package/src/lib/model/staff-overview.d.ts +54 -0
  74. package/src/lib/model/staff-overview.js +21 -0
  75. package/src/lib/model/staff-overview.js.map +1 -0
  76. package/src/lib/model/update-state.d.ts +119 -0
  77. package/src/lib/model/update-state.js +132 -0
  78. package/src/lib/model/update-state.js.map +1 -0
  79. package/src/lib/plan-entitlements.d.ts +46 -0
  80. package/src/lib/plan-entitlements.js +63 -0
  81. package/src/lib/plan-entitlements.js.map +1 -0
  82. package/src/lib/plugin.js +42 -0
  83. package/src/lib/plugin.js.map +1 -1
  84. package/src/lib/server/admin-overview.d.ts +18 -0
  85. package/src/lib/server/admin-overview.js +152 -0
  86. package/src/lib/server/admin-overview.js.map +1 -0
  87. package/src/lib/server/admin-reviews.js +4 -3
  88. package/src/lib/server/admin-reviews.js.map +1 -1
  89. package/src/lib/server/billing-webhook.js +50 -81
  90. package/src/lib/server/billing-webhook.js.map +1 -1
  91. package/src/lib/server/checkout.js +2 -2
  92. package/src/lib/server/checkout.js.map +1 -1
  93. package/src/lib/server/connect.js +3 -3
  94. package/src/lib/server/connect.js.map +1 -1
  95. package/src/lib/server/install-plugin.js +2 -1
  96. package/src/lib/server/install-plugin.js.map +1 -1
  97. package/src/lib/server/install-template.js +2 -1
  98. package/src/lib/server/install-template.js.map +1 -1
  99. package/src/lib/server/install-theme.js +1 -1
  100. package/src/lib/server/install-theme.js.map +1 -1
  101. package/src/lib/server/listing-versions.js +1 -1
  102. package/src/lib/server/listing-versions.js.map +1 -1
  103. package/src/lib/server/publish-email-starter.js +1 -1
  104. package/src/lib/server/publish-email-starter.js.map +1 -1
  105. package/src/lib/server/publish-preconditions.js +3 -3
  106. package/src/lib/server/publish-preconditions.js.map +1 -1
  107. package/src/lib/server/publish-template.js +5 -5
  108. package/src/lib/server/publish-template.js.map +1 -1
  109. package/src/lib/server/publish-theme.js +1 -1
  110. package/src/lib/server/publish-theme.js.map +1 -1
  111. package/src/lib/server/publisher-erasure.d.ts +62 -0
  112. package/src/lib/server/publisher-erasure.js +97 -0
  113. package/src/lib/server/publisher-erasure.js.map +1 -0
  114. package/src/lib/server/publisher-profile-save.js +1 -1
  115. package/src/lib/server/publisher-profile-save.js.map +1 -1
  116. package/src/lib/server/publisher-profile.d.ts +1 -1
  117. package/src/lib/server/publisher-profile.js.map +1 -1
  118. package/src/lib/server/revenue-source.d.ts +107 -0
  119. package/src/lib/server/revenue-source.js +236 -0
  120. package/src/lib/server/revenue-source.js.map +1 -0
  121. package/src/lib/server/tax-return-source.d.ts +134 -0
  122. package/src/lib/server/tax-return-source.js +309 -0
  123. package/src/lib/server/tax-return-source.js.map +1 -0
  124. package/src/lib/server/update-artifact.js +2 -2
  125. package/src/lib/server/update-artifact.js.map +1 -1
  126. package/src/lib/server.d.ts +1 -0
  127. package/src/lib/server.js +24 -0
  128. package/src/lib/server.js.map +1 -1
package/README.md CHANGED
@@ -14,12 +14,17 @@ Peer dependencies: `react`, `@mui/material`, `firebase`, `next`, and `firebase-a
14
14
 
15
15
  **On a published site.** Nothing of its own. What a listing installs (a component, site template, layout, theme, dataset schema, email template, email starter or plugin) is copied into the site's own collections and renders through the normal pipeline, so there is no separate site bundle.
16
16
 
17
- **Console** (`registerMarketplaceConsole`, the `console` registrar in `plugins.config.json`). The plugin adds no nav item. It exposes its UI through widget slots that the console app renders without importing the plugin:
17
+ **Console** (`registerMarketplaceConsole`, the `console` registrar in `plugins.config.json`). The Marketplace hub at `/[orgSlug]/marketplace` is one `orgNavItems` entry, served by the console's generic org plugin route: browse, installed, licenses, and the seller sections. The report and review queues are `staffPages`. On console pages that are not the marketplace, it draws into widget slots the console renders without importing the plugin:
18
18
 
19
- - `orgMarketplace`: browse and install, at organization scope.
20
- - `marketplaceListing`: the content of one listing's detail page.
21
- - `orgAddons`: the installed add-ons card, with upgrade, uninstall and sharing an install with the whole organization.
19
+ - `hostArtifactPublish`: the publish dialog for something a console page holds (a layout, a theme, a site template).
20
+ - `orgPluginInstalls`: the workspace's installed marketplace plugins, on its Plugins page.
21
+ - `pluginInstallStatus`: whether an installation runs the newest installable version, on its installation page.
22
22
  - `pluginSiteSet`: which sites one installation applies to.
23
+ - `staffOverview`: recent purchases and the refund-reversal recovery queue, on the staff overview.
24
+ - `templateGallery`: the "Marketplace templates" shelf of a site's template gallery, installing through `install-template`.
25
+ - `templateInstallStatus`: "Update available" on a library row whose template it installed, re-installing through `install-template`.
26
+
27
+ It declares `templateSource` in `plugins.config.json`: the `source.type` its template install stamps, and the "Marketplace" badge a site's library shows for it.
23
28
 
24
29
  It also registers a `rating` custom field type (`registerCustomFieldType`) with a starred input.
25
30
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aglyn/plugins-marketplace",
3
- "version": "1.0.0-beta.218",
3
+ "version": "1.0.0-beta.219",
4
4
  "license": "Apache-2.0",
5
5
  "homepage": "https://aglyn.com",
6
6
  "repository": {
@@ -25,19 +25,19 @@
25
25
  "./package.json": "./package.json"
26
26
  },
27
27
  "dependencies": {
28
- "@aglyn/aglyn": "1.0.0-beta.218",
29
- "@aglyn/aglyn-markdown-editor": "1.0.0-beta.218",
30
- "@aglyn/shared-data-enums": "1.0.0-beta.218",
31
- "@aglyn/shared-data-mdi": "1.0.0-beta.218",
32
- "@aglyn/shared-ui-jsx": "1.0.0-beta.218",
33
- "@aglyn/shared-ui-next": "1.0.0-beta.218",
34
- "@aglyn/shared-ui-snackstack": "1.0.0-beta.218",
35
- "@aglyn/shared-util-email": "1.0.0-beta.218",
36
- "@aglyn/shared-util-http": "1.0.0-beta.218",
37
- "@aglyn/shared-util-timestamp": "1.0.0-beta.218",
38
- "@aglyn/tenant-data-admin": "1.0.0-beta.218",
39
- "@aglyn/tenant-feature-instance": "1.0.0-beta.218",
40
- "@aglyn/tenant-runtime": "1.0.0-beta.218",
28
+ "@aglyn/aglyn": "1.0.0-beta.219",
29
+ "@aglyn/aglyn-markdown-editor": "1.0.0-beta.219",
30
+ "@aglyn/shared-data-enums": "1.0.0-beta.219",
31
+ "@aglyn/shared-data-mdi": "1.0.0-beta.219",
32
+ "@aglyn/shared-ui-jsx": "1.0.0-beta.219",
33
+ "@aglyn/shared-ui-next": "1.0.0-beta.219",
34
+ "@aglyn/shared-ui-snackstack": "1.0.0-beta.219",
35
+ "@aglyn/shared-util-email": "1.0.0-beta.219",
36
+ "@aglyn/shared-util-http": "1.0.0-beta.219",
37
+ "@aglyn/shared-util-timestamp": "1.0.0-beta.219",
38
+ "@aglyn/tenant-data-admin": "1.0.0-beta.219",
39
+ "@aglyn/tenant-feature-instance": "1.0.0-beta.219",
40
+ "@aglyn/tenant-runtime": "1.0.0-beta.219",
41
41
  "@swc/helpers": "0.5.23"
42
42
  },
43
43
  "peerDependencies": {
package/src/index.d.ts CHANGED
@@ -17,3 +17,4 @@
17
17
  export * from './lib/constants/bundle-common';
18
18
  export * from './lib/plugin';
19
19
  export * from './lib/model';
20
+ export type { marketplacePlanEntitlements } from './lib/plan-entitlements';
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../libs/plugins/marketplace/src/index.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\nexport * from './lib/constants/bundle-common'\nexport * from './lib/plugin'\nexport * from './lib/model'\n"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,cAAc,mCAA+B;AAC7C,cAAc,kBAAc;AAC5B,cAAc,uBAAa"}
1
+ {"version":3,"sources":["../../../../../libs/plugins/marketplace/src/index.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\nexport * from './lib/constants/bundle-common'\nexport * from './lib/plugin'\nexport * from './lib/model'\n// Type-only (AGL-3080): the plugin's entitlement keys are declared by module\n// augmentation in this module, so every program that loads the package\n// type-checks them. The figures reach core through the manifest generator.\nexport type { marketplacePlanEntitlements } from './lib/plan-entitlements'\n"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,cAAc,mCAA+B;AAC7C,cAAc,kBAAc;AAC5B,cAAc,uBAAa"}
@@ -22,10 +22,11 @@ import { useSnackbar } from "@aglyn/shared-ui-snackstack";
22
22
  import { Alert, Button, Chip, Stack, Tooltip, Typography } from "@mui/material";
23
23
  import { collection, doc, documentId, limit, query, where } from "firebase/firestore";
24
24
  import { useCallback, useMemo, useState } from "react";
25
- import { compareArtifactVersions, isPluginRevoked, lockdownRefusalText, offeredPluginVersion, parseLockdownRefusal } from "@aglyn/aglyn";
25
+ import { isPluginRevoked, lockdownRefusalText, offeredPluginVersion, parseLockdownRefusal } from "@aglyn/aglyn";
26
26
  import { useFirestore, useFirestoreCollection, useHostOrgId, useUser } from "@aglyn/tenant-feature-instance";
27
27
  import { writeSiteWideChange } from "@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change";
28
28
  import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
29
+ import { compareArtifactVersions } from "../model/update-state.js";
29
30
  /**
30
31
  * Installed marketplace plugins (AGL-45): lists the host's pinned plugin
31
32
  * installs with their capabilities, flags available upgrades (latest
@@ -371,7 +372,7 @@ import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
371
372
  installs.length === 0 && orgInstalls.length === 0 ? /*#__PURE__*/ _jsx(Typography, {
372
373
  variant: "body2",
373
374
  color: "text.secondary",
374
- children: 'No marketplace plugins installed. Install one from the ' + 'marketplace, then add a Plugin element to a screen and set ' + 'its listing id.'
375
+ children: 'No marketplace plugins installed. Install one from the ' + 'marketplace, then add a Plugin element to a page and set ' + 'its listing id.'
375
376
  }) : installs.map((install)=>{
376
377
  var _compareArtifactVersions, _ref, _ref1, _install_displayName, _ref2, _install_displayName1;
377
378
  var _install_manifest, _capabilities_network, _capabilities_events, _capabilities_network1, _capabilities_events1, _revocationDetailByListing_install_$id, _revocationDetailByListing_install_$id1, _revocationDetailByListing_install_$id2, _revocationDetailByListing_install_$id3;
@@ -561,7 +562,7 @@ import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
561
562
  /*#__PURE__*/ _jsx(Typography, {
562
563
  variant: "caption",
563
564
  color: "text.secondary",
564
- children: `Place a plugin on a screen with the Plugin element (component id ` + `"${PLUGIN_COMPONENT_ID}"), set to the listing id above.`
565
+ children: `Place a plugin on a page with the Plugin element (component id ` + `"${PLUGIN_COMPONENT_ID}"), set to the listing id above.`
565
566
  })
566
567
  ]
567
568
  })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/components/host-plugins-card.component.tsx"],"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'use client'\n\nimport {\n buildRoute,\n PLUGIN_COMPONENT_ID,\n pluginDocsHelp,\n Route,\n} from '@aglyn/aglyn'\nimport {\n AppLink,\n CardDisplay,\n useConfirmationContext,\n} from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { Alert, Button, Chip, Stack, Tooltip, Typography } from '@mui/material'\nimport {\n collection,\n doc,\n documentId,\n limit,\n query,\n where,\n} from 'firebase/firestore'\nimport { useCallback, useMemo, useState } from 'react'\nimport {\n compareArtifactVersions,\n isPluginRevoked,\n lockdownRefusalText,\n offeredPluginVersion,\n parseLockdownRefusal,\n} from '@aglyn/aglyn'\nimport {\n useFirestore,\n useFirestoreCollection,\n useHostOrgId,\n useUser,\n} from '@aglyn/tenant-feature-instance'\nimport { writeSiteWideChange } from '@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\n\nexport interface HostPluginsCardProps {\n hostId: string\n /**\n * Org slug from the URL, when this card is rendered at org scope. With it,\n * each row links through to that installation's own page (AGL-1007);\n * without it the rows stay plain text rather than linking to nowhere.\n */\n orgSlug?: string\n}\n\n/**\n * Installed marketplace plugins (AGL-45): lists the host's pinned plugin\n * installs with their capabilities, flags available upgrades (latest\n * listing version ≠ pinned) and platform kill switches (public\n * `revocations` read), and offers explicit upgrade/uninstall. Upgrades go\n * through `/api/marketplace/install-plugin` so the new pin is validated\n * server-side; uninstall removes the host install doc. Place the installed\n * plugin on a screen with the Plugin element (listing id).\n */\nexport function HostPluginsCard(props: HostPluginsCardProps) {\n const { hostId, orgSlug } = props\n // One row → one installation page (AGL-1007). The name is the link, so a\n // row stays clickable without swallowing its own action buttons.\n const installationHref = (listingId: string) =>\n orgSlug\n ? buildRoute(Route.ORG_PLUGIN_INSTALLATION, {\n orgSlug,\n pluginRef: listingId,\n })\n : undefined\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const [busy, setBusy] = useState<string | null>(null)\n\n const orgId = useHostOrgId(hostId)\n const { data: installDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'installs'), limit(50)),\n [firestore, hostId],\n { idField: '$id' },\n )\n // Org-tier pins (AGL-237): apply to every host in the org; a host pin\n // of the same listing shadows the org one. Held at null while\n // `useHostOrgId` is in flight, never `orgs/-pending-` (AGL-1440):\n // installs are member-gated, so the sentinel was a guaranteed-denied\n // listen on every mount of this card.\n const { data: orgInstallDocs } = useFirestoreCollection<any>(\n () =>\n orgId\n ? query(collection(firestore, 'orgs', orgId, 'installs'), limit(50))\n : null,\n [firestore, orgId],\n { idField: '$id' },\n )\n const orgInstalls = useMemo(\n () => [...((orgInstallDocs as any[]) ?? [])],\n [orgInstallDocs],\n )\n const installs = useMemo(\n () => [...((installDocs as any[]) ?? [])],\n [installDocs],\n )\n const listingIds = useMemo(\n () =>\n [\n ...new Set([\n ...installs.map((install) => install.$id),\n ...orgInstalls.map((install) => install.$id),\n ]),\n ].slice(0, 10),\n [installs, orgInstalls],\n )\n\n // Latest published version per listing (upgrade detection) — public read.\n const { data: listingDocs } = useFirestoreCollection<any>(\n () =>\n query(\n collection(firestore, 'marketplaceListings'),\n where(documentId(), 'in', listingIds.length ? listingIds : ['-none-']),\n ),\n [firestore, listingIds.join(',')],\n { idField: '$id' },\n )\n // Kill switches (public revocations read) for the installed listings.\n const { data: revocationDocs } = useFirestoreCollection<any>(\n () =>\n query(\n collection(firestore, 'revocations'),\n where(documentId(), 'in', listingIds.length ? listingIds : ['-none-']),\n ),\n [firestore, listingIds.join(',')],\n { idField: '$id' },\n )\n const revokedByListing = useMemo(() => {\n const map: Record<string, boolean> = {}\n for (const install of installs) {\n const revocation = ((revocationDocs as any[]) ?? []).find(\n (doc) => doc.$id === install.$id,\n )\n if (!revocation) continue\n // THE SHARED PREDICATE, not a fourth copy of it (AGL-2305). This card\n // carried a byte-for-byte duplicate of `isPluginRevoked`'s body, which\n // is precisely what AGL-1085 collapsed three inline copies into one to\n // stop: a reader that disagrees with the writer about this document's\n // shape is how a kill switch silently stops killing. It happened to be\n // correct; being correct today is not the property that matters.\n map[install.$id] = isPluginRevoked(revocation, install.version)\n }\n return map\n }, [installs, revocationDocs])\n\n /*==========================================\n * WHY IT WAS DISABLED (AGL-2328).\n *\n * Staff are REQUIRED to type a reason to revoke a plugin version — the\n * route refuses without one — and until now the customer whose site the\n * plugin stopped working on was shown a bare \"disabled\" chip and a generic\n * paragraph. The reason was stored, in the same document this card already\n * reads, one field away from the flag it renders. Every consumer of\n * `revocations` reads `versions` and nothing else; the type's own comment\n * calls `versions` \"the ONLY field any reader consults\", which was true and\n * is the defect.\n *\n * Only the REASON and the DATE are surfaced. `revokedBy` is a staff uid and\n * belongs in the audit log, not on a customer's screen.\n *=========================================*/\n const revocationDetailByListing = useMemo(() => {\n const map: Record<string, { reason: string | null; atMs: number | null }> =\n {}\n for (const doc of ((revocationDocs as any[]) ?? [])) {\n const at = doc.revokedAt\n map[doc.$id] = {\n reason:\n typeof doc.reason === 'string' && doc.reason.trim()\n ? doc.reason.trim()\n : null,\n atMs:\n at?.toMillis?.() ?? (typeof at?.seconds === 'number' ? at.seconds * 1000 : null),\n }\n }\n return map\n }, [revocationDocs])\n\n // The newest INSTALLABLE version, not merely the newest approved one\n // (AGL-1016, corrected by AGL-2368).\n //\n // Offering `latestVersion` here put an Upgrade button in front of every\n // workspace the moment a publisher uploaded, and the install route then\n // refused the click — AGL-966 only ever hands out reviewed bytes. The\n // mirror fixed that and then reproduced it one state over: nothing cleared\n // it when a version was revoked, so the Upgrade button pointed at bytes the\n // kill switch had stopped. Every writer of the mirror now derives it from\n // `newestInstallableVersion`, and this card holds the revocation document\n // ALREADY — it is right there, feeding the disabled chip below — so it\n // cross-checks rather than trusting a cache written by a separate,\n // non-transactional write it can observe mid-flight.\n const latestByListing = useMemo(() => {\n const map: Record<string, string> = {}\n for (const listing of (listingDocs as any[]) ?? []) {\n const revocation = ((revocationDocs as any[]) ?? []).find(\n (doc) => doc.$id === listing.$id,\n )\n map[listing.$id] = offeredPluginVersion(listing, revocation) ?? ''\n }\n return map\n }, [listingDocs, revocationDocs])\n\n const handleUpgrade = useCallback(\n (install: any) => async () => {\n setBusy(install.$id)\n try {\n const response = await authorizedFetch(\n user,\n '/api/marketplace/install-plugin',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId, listingId: install.$id }),\n },\n )\n const payload = await response.json().catch(() => ({}))\n if (response.ok) {\n enqueueSnackbar(`Upgraded to ${payload.version}`, {\n variant: 'success',\n persist: false,\n })\n } else {\n // An installs lockdown refuses the upgrade route too (AGL-1532).\n const locked = parseLockdownRefusal(response.status, payload)\n if (locked) {\n enqueueSnackbar(lockdownRefusalText(locked), {\n variant: 'warning',\n persist: true,\n })\n } else {\n enqueueSnackbar(payload?.error ?? 'Upgrade failed', {\n variant: 'error',\n allowDuplicate: true,\n })\n }\n }\n } finally {\n setBusy(null)\n }\n },\n [hostId, user, enqueueSnackbar],\n )\n\n const requestPluginApi = useCallback(\n async (body: Record<string, unknown>) => {\n const response = await authorizedFetch(\n user,\n '/api/marketplace/install-plugin',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId, ...body }),\n },\n )\n const payload = await response.json().catch(() => ({}))\n if (!response.ok) {\n // Every install-class action in this card funnels through here\n // (share, org upgrade, uninstall at both scopes) — so does the\n // pause notice (AGL-1532).\n const locked = parseLockdownRefusal(response.status, payload)\n if (locked) {\n enqueueSnackbar(lockdownRefusalText(locked), {\n variant: 'warning',\n persist: true,\n })\n return null\n }\n enqueueSnackbar(payload?.error ?? 'Plugin operation failed', {\n variant: 'error',\n allowDuplicate: true,\n })\n return null\n }\n return payload\n },\n [hostId, user, enqueueSnackbar],\n )\n\n // Promote a host pin to the org tier (AGL-237): org pin first, then the\n // host pin is removed — host pins shadow org ones in the loader.\n const handleShareWithOrg = useCallback(\n (install: any) => async () => {\n setBusy(install.$id)\n try {\n const payload = await requestPluginApi({\n listingId: install.$id,\n scope: 'org',\n })\n if (!payload) return\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.delete(\n doc(firestore, 'hosts', hostId, 'installs', install.$id),\n ),\n })\n enqueueSnackbar('Plugin now installed for the whole organization', {\n variant: 'success',\n persist: false,\n })\n } finally {\n setBusy(null)\n }\n },\n [requestPluginApi, firestore, hostId, user, enqueueSnackbar],\n )\n\n const handleOrgUpgrade = useCallback(\n (install: any) => async () => {\n setBusy(install.$id)\n try {\n const payload = await requestPluginApi({\n listingId: install.$id,\n scope: 'org',\n })\n if (payload) {\n enqueueSnackbar(`Upgraded to ${payload.version}`, {\n variant: 'success',\n persist: false,\n })\n }\n } finally {\n setBusy(null)\n }\n },\n [requestPluginApi, enqueueSnackbar],\n )\n\n const handleOrgUninstall = useCallback(\n (install: any) => async () => {\n const confirmed = await confirm({\n title: `Uninstall \"${install.displayName ?? install.$id}\" org-wide?`,\n description:\n 'Every site in the organization loses this plugin unless it has ' +\n 'its own host-level pin. Data the plugin created stays untouched.',\n confirmationText: 'Uninstall',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n const payload = await requestPluginApi({\n listingId: install.$id,\n scope: 'org',\n action: 'uninstall',\n })\n if (payload) {\n enqueueSnackbar('Plugin uninstalled org-wide', {\n variant: 'success',\n persist: false,\n })\n }\n },\n [confirm, requestPluginApi, enqueueSnackbar],\n )\n\n const handleUninstall = useCallback(\n (install: any) => async () => {\n const confirmed = await confirm({\n title: `Uninstall \"${install.displayName ?? install.$id}\"?`,\n description:\n 'Plugin elements pointing at it will show a placeholder until ' +\n 'reinstalled. Data the plugin created stays untouched.',\n confirmationText: 'Uninstall',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n // Through the API (AGL-424) so the org enablement switchboard stays\n // in step with the removed pin.\n const payload = await requestPluginApi({\n listingId: install.$id,\n action: 'uninstall',\n })\n if (payload) {\n enqueueSnackbar('Plugin uninstalled', {\n variant: 'success',\n persist: false,\n })\n }\n },\n [confirm, requestPluginApi, enqueueSnackbar],\n )\n\n return (\n <CardDisplay\n header=\"Marketplace installs\"\n help={pluginDocsHelp('plugins', { anchor: '#install--upgrade' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1.5}>\n {/* First-party plugins used to be re-listed here for context; that\n moved out (AGL-802) now that the switchboard sits directly above\n this card in Marketplace › Installed. This card is only the\n marketplace install pins. */}\n {/* \"Nothing installed\" has to mean nothing at either scope. This\n asked only about HOST pins, so an org-wide install printed the\n empty-state sentence directly above the row it was denying\n (AGL-1011) — most visible at org scope, which is where installs\n usually land org-wide. */}\n {installs.length === 0 && orgInstalls.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No marketplace plugins installed. Install one from the ' +\n 'marketplace, then add a Plugin element to a screen and set ' +\n 'its listing id.'}\n </Typography>\n ) : (\n installs.map((install) => {\n const latest = latestByListing[install.$id]\n const canUpgrade =\n (compareArtifactVersions(install.version, latest) ?? 0) < 0\n const revoked = revokedByListing[install.$id]\n const capabilities = install.manifest?.capabilities ?? {}\n return (\n <Stack\n key={install.$id}\n spacing={0.5}\n sx={{\n border: '1px solid',\n borderColor: 'divider',\n borderRadius: 1,\n p: 1.5,\n }}\n >\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Typography variant=\"body2\" sx={{ flex: 1 }} noWrap>\n {installationHref(install.$id) ? (\n <AppLink\n href={installationHref(install.$id)}\n color=\"inherit\"\n underline=\"hover\"\n >\n {install.displayName ?? install.pluginId ?? install.$id}\n </AppLink>\n ) : (\n (install.displayName ?? install.pluginId ?? install.$id)\n )}\n </Typography>\n <Chip size=\"small\" label={`v${install.version}`} />\n {revoked ? (\n <Chip size=\"small\" color=\"error\" label=\"disabled\" />\n ) : null}\n {canUpgrade ? (\n <Button\n size=\"small\"\n disabled={busy === install.$id}\n onClick={handleUpgrade(install)}\n >\n {`Upgrade to v${latest}`}\n </Button>\n ) : null}\n {orgId &&\n !orgInstalls.some((entry) => entry.$id === install.$id) ? (\n <Tooltip title=\"Install for every site in the organization and remove this site's own pin\">\n <Button\n size=\"small\"\n disabled={busy === install.$id}\n onClick={handleShareWithOrg(install)}\n >\n {'Share with org'}\n </Button>\n </Tooltip>\n ) : null}\n <Button\n size=\"small\"\n color=\"error\"\n onClick={handleUninstall(install)}\n >\n {'Uninstall'}\n </Button>\n </Stack>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {`id: ${install.$id}`}\n </Typography>\n {(capabilities.network?.length ||\n capabilities.events?.length) && (\n <Tooltip title=\"Declared in the plugin manifest, enforced by the sandbox\">\n <Typography variant=\"caption\" color=\"text.secondary\">\n {[\n capabilities.network?.length\n ? `network: ${capabilities.network.join(', ')}`\n : null,\n capabilities.events?.length\n ? `events: ${capabilities.events.join(', ')}`\n : null,\n ]\n .filter(Boolean)\n .join(' · ')}\n </Typography>\n </Tooltip>\n )}\n {revoked ? (\n <Alert severity=\"error\" sx={{ mt: 0.5 }}>\n {'The platform disabled this plugin version. It renders ' +\n 'a placeholder on your site until you upgrade or ' +\n 'uninstall it.'}\n {/*\n The reason staff were required to type, shown to the\n person it happened to (AGL-2328). Rendered only when one\n was recorded — a revocation written before the reason\n was required says so, rather than showing an empty\n quotation that reads as \"no reason given\".\n */}\n {revocationDetailByListing[install.$id]?.reason ? (\n <Typography variant=\"body2\" sx={{ mt: 1 }}>\n {`Reason: ${revocationDetailByListing[install.$id]?.reason}`}\n </Typography>\n ) : null}\n {revocationDetailByListing[install.$id]?.atMs ? (\n <Typography variant=\"caption\" sx={{ display: 'block', mt: 0.5 }}>\n {`Disabled ${new Date(\n revocationDetailByListing[install.$id]\n ?.atMs as number,\n ).toLocaleDateString()}`}\n </Typography>\n ) : null}\n </Alert>\n ) : null}\n </Stack>\n )\n })\n )}\n {orgInstalls.map((install) => {\n const latest = latestByListing[install.$id]\n const canUpgrade =\n (compareArtifactVersions(install.version, latest) ?? 0) < 0\n const shadowed = installs.some((entry) => entry.$id === install.$id)\n return (\n <Stack\n key={`org-${install.$id}`}\n spacing={0.5}\n sx={{\n border: '1px solid',\n borderColor: 'divider',\n borderRadius: 1,\n p: 1.5,\n }}\n >\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\" sx={{ flex: 1 }} noWrap>\n {installationHref(install.$id) ? (\n <AppLink\n href={installationHref(install.$id)}\n color=\"inherit\"\n underline=\"hover\"\n >\n {install.displayName ?? install.pluginId ?? install.$id}\n </AppLink>\n ) : (\n (install.displayName ?? install.pluginId ?? install.$id)\n )}\n </Typography>\n <Chip size=\"small\" color=\"primary\" label=\"Organization\" />\n <Chip size=\"small\" label={`v${install.version}`} />\n {shadowed ? (\n <Tooltip title=\"This site has its own pin, which takes precedence here\">\n <Chip size=\"small\" variant=\"outlined\" label=\"shadowed\" />\n </Tooltip>\n ) : null}\n {canUpgrade ? (\n <Button\n size=\"small\"\n disabled={busy === install.$id}\n onClick={handleOrgUpgrade(install)}\n >\n {`Upgrade to v${latest}`}\n </Button>\n ) : null}\n <Button\n size=\"small\"\n color=\"error\"\n onClick={handleOrgUninstall(install)}\n >\n {'Uninstall'}\n </Button>\n </Stack>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {`id: ${install.$id} · shared with every site in the org`}\n </Typography>\n </Stack>\n )\n })}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Place a plugin on a screen with the Plugin element (component id ` +\n `\"${PLUGIN_COMPONENT_ID}\"), set to the listing id above.`}\n </Typography>\n </Stack>\n </CardDisplay>\n )\n}\nHostPluginsCard.displayName = 'HostPluginsCard'\n\nexport default HostPluginsCard\n"],"names":["buildRoute","PLUGIN_COMPONENT_ID","pluginDocsHelp","Route","AppLink","CardDisplay","useConfirmationContext","useSnackbar","Alert","Button","Chip","Stack","Tooltip","Typography","collection","doc","documentId","limit","query","where","useCallback","useMemo","useState","compareArtifactVersions","isPluginRevoked","lockdownRefusalText","offeredPluginVersion","parseLockdownRefusal","useFirestore","useFirestoreCollection","useHostOrgId","useUser","writeSiteWideChange","authorizedFetch","HostPluginsCard","props","hostId","orgSlug","installationHref","listingId","ORG_PLUGIN_INSTALLATION","pluginRef","undefined","firestore","data","user","enqueueSnackbar","confirm","busy","setBusy","orgId","installDocs","idField","orgInstallDocs","orgInstalls","installs","listingIds","Set","map","install","$id","slice","listingDocs","length","join","revocationDocs","revokedByListing","revocation","find","version","revocationDetailByListing","at","revokedAt","reason","trim","atMs","toMillis","seconds","latestByListing","listing","handleUpgrade","response","method","headers","body","JSON","stringify","payload","json","catch","ok","variant","persist","locked","status","error","allowDuplicate","requestPluginApi","handleShareWithOrg","scope","write","batch","delete","handleOrgUpgrade","handleOrgUninstall","confirmed","title","displayName","description","confirmationText","confirmationButtonProps","color","then","action","handleUninstall","header","help","anchor","contentGutterX","contentGutterY","spacing","capabilities","latest","canUpgrade","revoked","manifest","sx","border","borderColor","borderRadius","p","direction","alignItems","flex","noWrap","href","underline","pluginId","size","label","disabled","onClick","some","entry","network","events","filter","Boolean","severity","mt","display","Date","toLocaleDateString","shadowed"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/components/host-plugins-card.component.tsx"],"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'use client'\n\nimport {\n buildRoute,\n PLUGIN_COMPONENT_ID,\n pluginDocsHelp,\n Route,\n} from '@aglyn/aglyn'\nimport {\n AppLink,\n CardDisplay,\n useConfirmationContext,\n} from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { Alert, Button, Chip, Stack, Tooltip, Typography } from '@mui/material'\nimport {\n collection,\n doc,\n documentId,\n limit,\n query,\n where,\n} from 'firebase/firestore'\nimport { useCallback, useMemo, useState } from 'react'\nimport {\n isPluginRevoked,\n lockdownRefusalText,\n offeredPluginVersion,\n parseLockdownRefusal,\n} from '@aglyn/aglyn'\nimport {\n useFirestore,\n useFirestoreCollection,\n useHostOrgId,\n useUser,\n} from '@aglyn/tenant-feature-instance'\nimport { writeSiteWideChange } from '@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { compareArtifactVersions } from '../model/update-state'\n\nexport interface HostPluginsCardProps {\n hostId: string\n /**\n * Org slug from the URL, when this card is rendered at org scope. With it,\n * each row links through to that installation's own page (AGL-1007);\n * without it the rows stay plain text rather than linking to nowhere.\n */\n orgSlug?: string\n}\n\n/**\n * Installed marketplace plugins (AGL-45): lists the host's pinned plugin\n * installs with their capabilities, flags available upgrades (latest\n * listing version ≠ pinned) and platform kill switches (public\n * `revocations` read), and offers explicit upgrade/uninstall. Upgrades go\n * through `/api/marketplace/install-plugin` so the new pin is validated\n * server-side; uninstall removes the host install doc. Place the installed\n * plugin on a screen with the Plugin element (listing id).\n */\nexport function HostPluginsCard(props: HostPluginsCardProps) {\n const { hostId, orgSlug } = props\n // One row → one installation page (AGL-1007). The name is the link, so a\n // row stays clickable without swallowing its own action buttons.\n const installationHref = (listingId: string) =>\n orgSlug\n ? buildRoute(Route.ORG_PLUGIN_INSTALLATION, {\n orgSlug,\n pluginRef: listingId,\n })\n : undefined\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const [busy, setBusy] = useState<string | null>(null)\n\n const orgId = useHostOrgId(hostId)\n const { data: installDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'installs'), limit(50)),\n [firestore, hostId],\n { idField: '$id' },\n )\n // Org-tier pins (AGL-237): apply to every host in the org; a host pin\n // of the same listing shadows the org one. Held at null while\n // `useHostOrgId` is in flight, never `orgs/-pending-` (AGL-1440):\n // installs are member-gated, so the sentinel was a guaranteed-denied\n // listen on every mount of this card.\n const { data: orgInstallDocs } = useFirestoreCollection<any>(\n () =>\n orgId\n ? query(collection(firestore, 'orgs', orgId, 'installs'), limit(50))\n : null,\n [firestore, orgId],\n { idField: '$id' },\n )\n const orgInstalls = useMemo(\n () => [...((orgInstallDocs as any[]) ?? [])],\n [orgInstallDocs],\n )\n const installs = useMemo(\n () => [...((installDocs as any[]) ?? [])],\n [installDocs],\n )\n const listingIds = useMemo(\n () =>\n [\n ...new Set([\n ...installs.map((install) => install.$id),\n ...orgInstalls.map((install) => install.$id),\n ]),\n ].slice(0, 10),\n [installs, orgInstalls],\n )\n\n // Latest published version per listing (upgrade detection) — public read.\n const { data: listingDocs } = useFirestoreCollection<any>(\n () =>\n query(\n collection(firestore, 'marketplaceListings'),\n where(documentId(), 'in', listingIds.length ? listingIds : ['-none-']),\n ),\n [firestore, listingIds.join(',')],\n { idField: '$id' },\n )\n // Kill switches (public revocations read) for the installed listings.\n const { data: revocationDocs } = useFirestoreCollection<any>(\n () =>\n query(\n collection(firestore, 'revocations'),\n where(documentId(), 'in', listingIds.length ? listingIds : ['-none-']),\n ),\n [firestore, listingIds.join(',')],\n { idField: '$id' },\n )\n const revokedByListing = useMemo(() => {\n const map: Record<string, boolean> = {}\n for (const install of installs) {\n const revocation = ((revocationDocs as any[]) ?? []).find(\n (doc) => doc.$id === install.$id,\n )\n if (!revocation) continue\n // THE SHARED PREDICATE, not a fourth copy of it (AGL-2305). This card\n // carried a byte-for-byte duplicate of `isPluginRevoked`'s body, which\n // is precisely what AGL-1085 collapsed three inline copies into one to\n // stop: a reader that disagrees with the writer about this document's\n // shape is how a kill switch silently stops killing. It happened to be\n // correct; being correct today is not the property that matters.\n map[install.$id] = isPluginRevoked(revocation, install.version)\n }\n return map\n }, [installs, revocationDocs])\n\n /*==========================================\n * WHY IT WAS DISABLED (AGL-2328).\n *\n * Staff are REQUIRED to type a reason to revoke a plugin version — the\n * route refuses without one — and until now the customer whose site the\n * plugin stopped working on was shown a bare \"disabled\" chip and a generic\n * paragraph. The reason was stored, in the same document this card already\n * reads, one field away from the flag it renders. Every consumer of\n * `revocations` reads `versions` and nothing else; the type's own comment\n * calls `versions` \"the ONLY field any reader consults\", which was true and\n * is the defect.\n *\n * Only the REASON and the DATE are surfaced. `revokedBy` is a staff uid and\n * belongs in the audit log, not on a customer's screen.\n *=========================================*/\n const revocationDetailByListing = useMemo(() => {\n const map: Record<string, { reason: string | null; atMs: number | null }> =\n {}\n for (const doc of ((revocationDocs as any[]) ?? [])) {\n const at = doc.revokedAt\n map[doc.$id] = {\n reason:\n typeof doc.reason === 'string' && doc.reason.trim()\n ? doc.reason.trim()\n : null,\n atMs:\n at?.toMillis?.() ?? (typeof at?.seconds === 'number' ? at.seconds * 1000 : null),\n }\n }\n return map\n }, [revocationDocs])\n\n // The newest INSTALLABLE version, not merely the newest approved one\n // (AGL-1016, corrected by AGL-2368).\n //\n // Offering `latestVersion` here put an Upgrade button in front of every\n // workspace the moment a publisher uploaded, and the install route then\n // refused the click — AGL-966 only ever hands out reviewed bytes. The\n // mirror fixed that and then reproduced it one state over: nothing cleared\n // it when a version was revoked, so the Upgrade button pointed at bytes the\n // kill switch had stopped. Every writer of the mirror now derives it from\n // `newestInstallableVersion`, and this card holds the revocation document\n // ALREADY — it is right there, feeding the disabled chip below — so it\n // cross-checks rather than trusting a cache written by a separate,\n // non-transactional write it can observe mid-flight.\n const latestByListing = useMemo(() => {\n const map: Record<string, string> = {}\n for (const listing of (listingDocs as any[]) ?? []) {\n const revocation = ((revocationDocs as any[]) ?? []).find(\n (doc) => doc.$id === listing.$id,\n )\n map[listing.$id] = offeredPluginVersion(listing, revocation) ?? ''\n }\n return map\n }, [listingDocs, revocationDocs])\n\n const handleUpgrade = useCallback(\n (install: any) => async () => {\n setBusy(install.$id)\n try {\n const response = await authorizedFetch(\n user,\n '/api/marketplace/install-plugin',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId, listingId: install.$id }),\n },\n )\n const payload = await response.json().catch(() => ({}))\n if (response.ok) {\n enqueueSnackbar(`Upgraded to ${payload.version}`, {\n variant: 'success',\n persist: false,\n })\n } else {\n // An installs lockdown refuses the upgrade route too (AGL-1532).\n const locked = parseLockdownRefusal(response.status, payload)\n if (locked) {\n enqueueSnackbar(lockdownRefusalText(locked), {\n variant: 'warning',\n persist: true,\n })\n } else {\n enqueueSnackbar(payload?.error ?? 'Upgrade failed', {\n variant: 'error',\n allowDuplicate: true,\n })\n }\n }\n } finally {\n setBusy(null)\n }\n },\n [hostId, user, enqueueSnackbar],\n )\n\n const requestPluginApi = useCallback(\n async (body: Record<string, unknown>) => {\n const response = await authorizedFetch(\n user,\n '/api/marketplace/install-plugin',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId, ...body }),\n },\n )\n const payload = await response.json().catch(() => ({}))\n if (!response.ok) {\n // Every install-class action in this card funnels through here\n // (share, org upgrade, uninstall at both scopes) — so does the\n // pause notice (AGL-1532).\n const locked = parseLockdownRefusal(response.status, payload)\n if (locked) {\n enqueueSnackbar(lockdownRefusalText(locked), {\n variant: 'warning',\n persist: true,\n })\n return null\n }\n enqueueSnackbar(payload?.error ?? 'Plugin operation failed', {\n variant: 'error',\n allowDuplicate: true,\n })\n return null\n }\n return payload\n },\n [hostId, user, enqueueSnackbar],\n )\n\n // Promote a host pin to the org tier (AGL-237): org pin first, then the\n // host pin is removed — host pins shadow org ones in the loader.\n const handleShareWithOrg = useCallback(\n (install: any) => async () => {\n setBusy(install.$id)\n try {\n const payload = await requestPluginApi({\n listingId: install.$id,\n scope: 'org',\n })\n if (!payload) return\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.delete(\n doc(firestore, 'hosts', hostId, 'installs', install.$id),\n ),\n })\n enqueueSnackbar('Plugin now installed for the whole organization', {\n variant: 'success',\n persist: false,\n })\n } finally {\n setBusy(null)\n }\n },\n [requestPluginApi, firestore, hostId, user, enqueueSnackbar],\n )\n\n const handleOrgUpgrade = useCallback(\n (install: any) => async () => {\n setBusy(install.$id)\n try {\n const payload = await requestPluginApi({\n listingId: install.$id,\n scope: 'org',\n })\n if (payload) {\n enqueueSnackbar(`Upgraded to ${payload.version}`, {\n variant: 'success',\n persist: false,\n })\n }\n } finally {\n setBusy(null)\n }\n },\n [requestPluginApi, enqueueSnackbar],\n )\n\n const handleOrgUninstall = useCallback(\n (install: any) => async () => {\n const confirmed = await confirm({\n title: `Uninstall \"${install.displayName ?? install.$id}\" org-wide?`,\n description:\n 'Every site in the organization loses this plugin unless it has ' +\n 'its own host-level pin. Data the plugin created stays untouched.',\n confirmationText: 'Uninstall',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n const payload = await requestPluginApi({\n listingId: install.$id,\n scope: 'org',\n action: 'uninstall',\n })\n if (payload) {\n enqueueSnackbar('Plugin uninstalled org-wide', {\n variant: 'success',\n persist: false,\n })\n }\n },\n [confirm, requestPluginApi, enqueueSnackbar],\n )\n\n const handleUninstall = useCallback(\n (install: any) => async () => {\n const confirmed = await confirm({\n title: `Uninstall \"${install.displayName ?? install.$id}\"?`,\n description:\n 'Plugin elements pointing at it will show a placeholder until ' +\n 'reinstalled. Data the plugin created stays untouched.',\n confirmationText: 'Uninstall',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n // Through the API (AGL-424) so the org enablement switchboard stays\n // in step with the removed pin.\n const payload = await requestPluginApi({\n listingId: install.$id,\n action: 'uninstall',\n })\n if (payload) {\n enqueueSnackbar('Plugin uninstalled', {\n variant: 'success',\n persist: false,\n })\n }\n },\n [confirm, requestPluginApi, enqueueSnackbar],\n )\n\n return (\n <CardDisplay\n header=\"Marketplace installs\"\n help={pluginDocsHelp('plugins', { anchor: '#install--upgrade' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1.5}>\n {/* First-party plugins used to be re-listed here for context; that\n moved out (AGL-802) now that the switchboard sits directly above\n this card in Marketplace › Installed. This card is only the\n marketplace install pins. */}\n {/* \"Nothing installed\" has to mean nothing at either scope. This\n asked only about HOST pins, so an org-wide install printed the\n empty-state sentence directly above the row it was denying\n (AGL-1011) — most visible at org scope, which is where installs\n usually land org-wide. */}\n {installs.length === 0 && orgInstalls.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No marketplace plugins installed. Install one from the ' +\n 'marketplace, then add a Plugin element to a page and set ' +\n 'its listing id.'}\n </Typography>\n ) : (\n installs.map((install) => {\n const latest = latestByListing[install.$id]\n const canUpgrade =\n (compareArtifactVersions(install.version, latest) ?? 0) < 0\n const revoked = revokedByListing[install.$id]\n const capabilities = install.manifest?.capabilities ?? {}\n return (\n <Stack\n key={install.$id}\n spacing={0.5}\n sx={{\n border: '1px solid',\n borderColor: 'divider',\n borderRadius: 1,\n p: 1.5,\n }}\n >\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Typography variant=\"body2\" sx={{ flex: 1 }} noWrap>\n {installationHref(install.$id) ? (\n <AppLink\n href={installationHref(install.$id)}\n color=\"inherit\"\n underline=\"hover\"\n >\n {install.displayName ?? install.pluginId ?? install.$id}\n </AppLink>\n ) : (\n (install.displayName ?? install.pluginId ?? install.$id)\n )}\n </Typography>\n <Chip size=\"small\" label={`v${install.version}`} />\n {revoked ? (\n <Chip size=\"small\" color=\"error\" label=\"disabled\" />\n ) : null}\n {canUpgrade ? (\n <Button\n size=\"small\"\n disabled={busy === install.$id}\n onClick={handleUpgrade(install)}\n >\n {`Upgrade to v${latest}`}\n </Button>\n ) : null}\n {orgId &&\n !orgInstalls.some((entry) => entry.$id === install.$id) ? (\n <Tooltip title=\"Install for every site in the organization and remove this site's own pin\">\n <Button\n size=\"small\"\n disabled={busy === install.$id}\n onClick={handleShareWithOrg(install)}\n >\n {'Share with org'}\n </Button>\n </Tooltip>\n ) : null}\n <Button\n size=\"small\"\n color=\"error\"\n onClick={handleUninstall(install)}\n >\n {'Uninstall'}\n </Button>\n </Stack>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {`id: ${install.$id}`}\n </Typography>\n {(capabilities.network?.length ||\n capabilities.events?.length) && (\n <Tooltip title=\"Declared in the plugin manifest, enforced by the sandbox\">\n <Typography variant=\"caption\" color=\"text.secondary\">\n {[\n capabilities.network?.length\n ? `network: ${capabilities.network.join(', ')}`\n : null,\n capabilities.events?.length\n ? `events: ${capabilities.events.join(', ')}`\n : null,\n ]\n .filter(Boolean)\n .join(' · ')}\n </Typography>\n </Tooltip>\n )}\n {revoked ? (\n <Alert severity=\"error\" sx={{ mt: 0.5 }}>\n {'The platform disabled this plugin version. It renders ' +\n 'a placeholder on your site until you upgrade or ' +\n 'uninstall it.'}\n {/*\n The reason staff were required to type, shown to the\n person it happened to (AGL-2328). Rendered only when one\n was recorded — a revocation written before the reason\n was required says so, rather than showing an empty\n quotation that reads as \"no reason given\".\n */}\n {revocationDetailByListing[install.$id]?.reason ? (\n <Typography variant=\"body2\" sx={{ mt: 1 }}>\n {`Reason: ${revocationDetailByListing[install.$id]?.reason}`}\n </Typography>\n ) : null}\n {revocationDetailByListing[install.$id]?.atMs ? (\n <Typography variant=\"caption\" sx={{ display: 'block', mt: 0.5 }}>\n {`Disabled ${new Date(\n revocationDetailByListing[install.$id]\n ?.atMs as number,\n ).toLocaleDateString()}`}\n </Typography>\n ) : null}\n </Alert>\n ) : null}\n </Stack>\n )\n })\n )}\n {orgInstalls.map((install) => {\n const latest = latestByListing[install.$id]\n const canUpgrade =\n (compareArtifactVersions(install.version, latest) ?? 0) < 0\n const shadowed = installs.some((entry) => entry.$id === install.$id)\n return (\n <Stack\n key={`org-${install.$id}`}\n spacing={0.5}\n sx={{\n border: '1px solid',\n borderColor: 'divider',\n borderRadius: 1,\n p: 1.5,\n }}\n >\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\" sx={{ flex: 1 }} noWrap>\n {installationHref(install.$id) ? (\n <AppLink\n href={installationHref(install.$id)}\n color=\"inherit\"\n underline=\"hover\"\n >\n {install.displayName ?? install.pluginId ?? install.$id}\n </AppLink>\n ) : (\n (install.displayName ?? install.pluginId ?? install.$id)\n )}\n </Typography>\n <Chip size=\"small\" color=\"primary\" label=\"Organization\" />\n <Chip size=\"small\" label={`v${install.version}`} />\n {shadowed ? (\n <Tooltip title=\"This site has its own pin, which takes precedence here\">\n <Chip size=\"small\" variant=\"outlined\" label=\"shadowed\" />\n </Tooltip>\n ) : null}\n {canUpgrade ? (\n <Button\n size=\"small\"\n disabled={busy === install.$id}\n onClick={handleOrgUpgrade(install)}\n >\n {`Upgrade to v${latest}`}\n </Button>\n ) : null}\n <Button\n size=\"small\"\n color=\"error\"\n onClick={handleOrgUninstall(install)}\n >\n {'Uninstall'}\n </Button>\n </Stack>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {`id: ${install.$id} · shared with every site in the org`}\n </Typography>\n </Stack>\n )\n })}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Place a plugin on a page with the Plugin element (component id ` +\n `\"${PLUGIN_COMPONENT_ID}\"), set to the listing id above.`}\n </Typography>\n </Stack>\n </CardDisplay>\n )\n}\nHostPluginsCard.displayName = 'HostPluginsCard'\n\nexport default HostPluginsCard\n"],"names":["buildRoute","PLUGIN_COMPONENT_ID","pluginDocsHelp","Route","AppLink","CardDisplay","useConfirmationContext","useSnackbar","Alert","Button","Chip","Stack","Tooltip","Typography","collection","doc","documentId","limit","query","where","useCallback","useMemo","useState","isPluginRevoked","lockdownRefusalText","offeredPluginVersion","parseLockdownRefusal","useFirestore","useFirestoreCollection","useHostOrgId","useUser","writeSiteWideChange","authorizedFetch","compareArtifactVersions","HostPluginsCard","props","hostId","orgSlug","installationHref","listingId","ORG_PLUGIN_INSTALLATION","pluginRef","undefined","firestore","data","user","enqueueSnackbar","confirm","busy","setBusy","orgId","installDocs","idField","orgInstallDocs","orgInstalls","installs","listingIds","Set","map","install","$id","slice","listingDocs","length","join","revocationDocs","revokedByListing","revocation","find","version","revocationDetailByListing","at","revokedAt","reason","trim","atMs","toMillis","seconds","latestByListing","listing","handleUpgrade","response","method","headers","body","JSON","stringify","payload","json","catch","ok","variant","persist","locked","status","error","allowDuplicate","requestPluginApi","handleShareWithOrg","scope","write","batch","delete","handleOrgUpgrade","handleOrgUninstall","confirmed","title","displayName","description","confirmationText","confirmationButtonProps","color","then","action","handleUninstall","header","help","anchor","contentGutterX","contentGutterY","spacing","capabilities","latest","canUpgrade","revoked","manifest","sx","border","borderColor","borderRadius","p","direction","alignItems","flex","noWrap","href","underline","pluginId","size","label","disabled","onClick","some","entry","network","events","filter","Boolean","severity","mt","display","Date","toLocaleDateString","shadowed"],"mappings":"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"}
@@ -19,7 +19,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
19
19
  import { LISTING_README_MAX_CHARS, listingArtifactType, installsUnreviewedFallback, listingArtifactLabel, listingInclusions, hasLivePurchaseOf, hasOrgLicenceOf, licensedOrgIdsFor, installTargetsFor, isListingDeleted, isPrivateListing, resolveInstallPlan, resolveOrgInstallSummary, resolvePluginInstallState, resolveUninstallTargets, safePublisherHref, safePublisherImageSrc } from "../model/marketplace.js";
20
20
  import UninstallImpactDialog from "./uninstall-impact-dialog.component.js";
21
21
  import { ListingImage } from "./listing-image.component.js";
22
- import { buildRoute, parseMarkdownLite, PLATFORM_BRAND_NAME, offeredPluginVersion, PLUGIN_HOST_ABI_VERSION, pluginDocsHelp, resolveMarkdownLink, resolveMediaSrc, resolveUpdateState, Route, updateStateLabel } from "@aglyn/aglyn";
22
+ import { buildRoute, parseMarkdownLite, PLATFORM_BRAND_NAME, offeredPluginVersion, PLUGIN_HOST_ABI_VERSION, pluginDocsHelp, resolveMarkdownLink, resolveMediaSrc, Route } from "@aglyn/aglyn";
23
23
  import { mdiCheckCircle, mdiCheckCircleOutline, mdiEmailOutline, mdiGithub, mdiInformationOutline, mdiLifebuoy, mdiLinkedin, mdiStorefrontOutline, mdiTwitter, mdiWeb } from "@aglyn/shared-data-mdi";
24
24
  import { AppLink, CardDisplay, GridItems, MdiIcon } from "@aglyn/shared-ui-jsx";
25
25
  import { NextPageTitle } from "@aglyn/shared-ui-next/contexts/next-page-title-provider";
@@ -41,6 +41,7 @@ import { MenuItem, TextField } from "@mui/material";
41
41
  import { useMarketplaceActions } from "../hooks/use-marketplace-actions.js";
42
42
  import { useArtifactUpdate } from "../hooks/use-artifact-update.js";
43
43
  import ArtifactUpdateDialog from "./artifact-update-dialog.component.js";
44
+ import { resolveUpdateState, updateStateLabel } from "../model/update-state.js";
44
45
  /**
45
46
  * How many of the site's components the page reads to answer one question.
46
47
  *