@aglyn/plugins-marketplace 1.0.0-beta.143

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 (165) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +7 -0
  3. package/package.json +51 -0
  4. package/src/index.d.ts +19 -0
  5. package/src/index.js +20 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/components/artifact-update-dialog.component.d.ts +27 -0
  8. package/src/lib/components/artifact-update-dialog.component.js +244 -0
  9. package/src/lib/components/artifact-update-dialog.component.js.map +1 -0
  10. package/src/lib/components/host-plugins-card.component.d.ts +23 -0
  11. package/src/lib/components/host-plugins-card.component.js +566 -0
  12. package/src/lib/components/host-plugins-card.component.js.map +1 -0
  13. package/src/lib/components/listing-content.component.d.ts +57 -0
  14. package/src/lib/components/listing-content.component.js +1880 -0
  15. package/src/lib/components/listing-content.component.js.map +1 -0
  16. package/src/lib/components/listing-image.component.d.ts +46 -0
  17. package/src/lib/components/listing-image.component.js +38 -0
  18. package/src/lib/components/listing-image.component.js.map +1 -0
  19. package/src/lib/components/listing-reviews.component.d.ts +22 -0
  20. package/src/lib/components/listing-reviews.component.js +419 -0
  21. package/src/lib/components/listing-reviews.component.js.map +1 -0
  22. package/src/lib/components/marketplace-browse.component.d.ts +38 -0
  23. package/src/lib/components/marketplace-browse.component.js +802 -0
  24. package/src/lib/components/marketplace-browse.component.js.map +1 -0
  25. package/src/lib/components/plugin-site-set-panel.component.d.ts +24 -0
  26. package/src/lib/components/plugin-site-set-panel.component.js +137 -0
  27. package/src/lib/components/plugin-site-set-panel.component.js.map +1 -0
  28. package/src/lib/components/plugin-site-set.component.d.ts +43 -0
  29. package/src/lib/components/plugin-site-set.component.js +425 -0
  30. package/src/lib/components/plugin-site-set.component.js.map +1 -0
  31. package/src/lib/components/rating-input.component.d.ts +3 -0
  32. package/src/lib/components/rating-input.component.js +47 -0
  33. package/src/lib/components/rating-input.component.js.map +1 -0
  34. package/src/lib/components/report-target.component.d.ts +36 -0
  35. package/src/lib/components/report-target.component.js +181 -0
  36. package/src/lib/components/report-target.component.js.map +1 -0
  37. package/src/lib/components/uninstall-impact-dialog.component.d.ts +40 -0
  38. package/src/lib/components/uninstall-impact-dialog.component.js +244 -0
  39. package/src/lib/components/uninstall-impact-dialog.component.js.map +1 -0
  40. package/src/lib/constants/bundle-common.d.ts +8 -0
  41. package/src/lib/constants/bundle-common.js +9 -0
  42. package/src/lib/constants/bundle-common.js.map +1 -0
  43. package/src/lib/hooks/use-artifact-update.d.ts +41 -0
  44. package/src/lib/hooks/use-artifact-update.js +169 -0
  45. package/src/lib/hooks/use-artifact-update.js.map +1 -0
  46. package/src/lib/hooks/use-marketplace-actions.d.ts +37 -0
  47. package/src/lib/hooks/use-marketplace-actions.js +523 -0
  48. package/src/lib/hooks/use-marketplace-actions.js.map +1 -0
  49. package/src/lib/model/artifact-merge.d.ts +154 -0
  50. package/src/lib/model/artifact-merge.js +228 -0
  51. package/src/lib/model/artifact-merge.js.map +1 -0
  52. package/src/lib/model/index.d.ts +43 -0
  53. package/src/lib/model/index.js +41 -0
  54. package/src/lib/model/index.js.map +1 -0
  55. package/src/lib/model/marketplace-props.d.ts +57 -0
  56. package/src/lib/model/marketplace-props.js +441 -0
  57. package/src/lib/model/marketplace-props.js.map +1 -0
  58. package/src/lib/model/marketplace.d.ts +833 -0
  59. package/src/lib/model/marketplace.js +929 -0
  60. package/src/lib/model/marketplace.js.map +1 -0
  61. package/src/lib/model/rating-field.d.ts +24 -0
  62. package/src/lib/model/rating-field.js +33 -0
  63. package/src/lib/model/rating-field.js.map +1 -0
  64. package/src/lib/plugin.d.ts +33 -0
  65. package/src/lib/plugin.js +86 -0
  66. package/src/lib/plugin.js.map +1 -0
  67. package/src/lib/server/billing-webhook.d.ts +25 -0
  68. package/src/lib/server/billing-webhook.js +1062 -0
  69. package/src/lib/server/billing-webhook.js.map +1 -0
  70. package/src/lib/server/checkout.d.ts +76 -0
  71. package/src/lib/server/checkout.js +477 -0
  72. package/src/lib/server/checkout.js.map +1 -0
  73. package/src/lib/server/connect.d.ts +24 -0
  74. package/src/lib/server/connect.js +205 -0
  75. package/src/lib/server/connect.js.map +1 -0
  76. package/src/lib/server/install-dataset-schema.d.ts +32 -0
  77. package/src/lib/server/install-dataset-schema.js +255 -0
  78. package/src/lib/server/install-dataset-schema.js.map +1 -0
  79. package/src/lib/server/install-email-starter.d.ts +65 -0
  80. package/src/lib/server/install-email-starter.js +279 -0
  81. package/src/lib/server/install-email-starter.js.map +1 -0
  82. package/src/lib/server/install-email-template.d.ts +34 -0
  83. package/src/lib/server/install-email-template.js +252 -0
  84. package/src/lib/server/install-email-template.js.map +1 -0
  85. package/src/lib/server/install-layout.d.ts +29 -0
  86. package/src/lib/server/install-layout.js +323 -0
  87. package/src/lib/server/install-layout.js.map +1 -0
  88. package/src/lib/server/install-pin-counts.d.ts +173 -0
  89. package/src/lib/server/install-pin-counts.js +248 -0
  90. package/src/lib/server/install-pin-counts.js.map +1 -0
  91. package/src/lib/server/install-plugin.d.ts +18 -0
  92. package/src/lib/server/install-plugin.js +405 -0
  93. package/src/lib/server/install-plugin.js.map +1 -0
  94. package/src/lib/server/install-template.d.ts +37 -0
  95. package/src/lib/server/install-template.js +348 -0
  96. package/src/lib/server/install-template.js.map +1 -0
  97. package/src/lib/server/install-theme.d.ts +42 -0
  98. package/src/lib/server/install-theme.js +313 -0
  99. package/src/lib/server/install-theme.js.map +1 -0
  100. package/src/lib/server/install.d.ts +27 -0
  101. package/src/lib/server/install.js +276 -0
  102. package/src/lib/server/install.js.map +1 -0
  103. package/src/lib/server/listing-versions.d.ts +26 -0
  104. package/src/lib/server/listing-versions.js +378 -0
  105. package/src/lib/server/listing-versions.js.map +1 -0
  106. package/src/lib/server/preview-image.d.ts +37 -0
  107. package/src/lib/server/preview-image.js +216 -0
  108. package/src/lib/server/preview-image.js.map +1 -0
  109. package/src/lib/server/provenance.d.ts +100 -0
  110. package/src/lib/server/provenance.js +118 -0
  111. package/src/lib/server/provenance.js.map +1 -0
  112. package/src/lib/server/publish-dataset-schema.d.ts +31 -0
  113. package/src/lib/server/publish-dataset-schema.js +202 -0
  114. package/src/lib/server/publish-dataset-schema.js.map +1 -0
  115. package/src/lib/server/publish-email-starter.d.ts +44 -0
  116. package/src/lib/server/publish-email-starter.js +231 -0
  117. package/src/lib/server/publish-email-starter.js.map +1 -0
  118. package/src/lib/server/publish-email-template.d.ts +33 -0
  119. package/src/lib/server/publish-email-template.js +211 -0
  120. package/src/lib/server/publish-email-template.js.map +1 -0
  121. package/src/lib/server/publish-layout.d.ts +33 -0
  122. package/src/lib/server/publish-layout.js +243 -0
  123. package/src/lib/server/publish-layout.js.map +1 -0
  124. package/src/lib/server/publish-plugin.d.ts +18 -0
  125. package/src/lib/server/publish-plugin.js +485 -0
  126. package/src/lib/server/publish-plugin.js.map +1 -0
  127. package/src/lib/server/publish-preconditions.d.ts +86 -0
  128. package/src/lib/server/publish-preconditions.js +160 -0
  129. package/src/lib/server/publish-preconditions.js.map +1 -0
  130. package/src/lib/server/publish-template.d.ts +27 -0
  131. package/src/lib/server/publish-template.js +215 -0
  132. package/src/lib/server/publish-template.js.map +1 -0
  133. package/src/lib/server/publish-theme.d.ts +38 -0
  134. package/src/lib/server/publish-theme.js +186 -0
  135. package/src/lib/server/publish-theme.js.map +1 -0
  136. package/src/lib/server/publish.d.ts +26 -0
  137. package/src/lib/server/publish.js +204 -0
  138. package/src/lib/server/publish.js.map +1 -0
  139. package/src/lib/server/publisher-profile-save.d.ts +30 -0
  140. package/src/lib/server/publisher-profile-save.js +226 -0
  141. package/src/lib/server/publisher-profile-save.js.map +1 -0
  142. package/src/lib/server/publisher-profile.d.ts +86 -0
  143. package/src/lib/server/publisher-profile.js +116 -0
  144. package/src/lib/server/publisher-profile.js.map +1 -0
  145. package/src/lib/server/purchase-entitlement.d.ts +83 -0
  146. package/src/lib/server/purchase-entitlement.js +118 -0
  147. package/src/lib/server/purchase-entitlement.js.map +1 -0
  148. package/src/lib/server/report.d.ts +32 -0
  149. package/src/lib/server/report.js +101 -0
  150. package/src/lib/server/report.js.map +1 -0
  151. package/src/lib/server/reviews.d.ts +41 -0
  152. package/src/lib/server/reviews.js +240 -0
  153. package/src/lib/server/reviews.js.map +1 -0
  154. package/src/lib/server/update-artifact.d.ts +19 -0
  155. package/src/lib/server/update-artifact.js +569 -0
  156. package/src/lib/server/update-artifact.js.map +1 -0
  157. package/src/lib/server/verification-request.d.ts +31 -0
  158. package/src/lib/server/verification-request.js +173 -0
  159. package/src/lib/server/verification-request.js.map +1 -0
  160. package/src/lib/server/version-stats.d.ts +202 -0
  161. package/src/lib/server/version-stats.js +305 -0
  162. package/src/lib/server/version-stats.js.map +1 -0
  163. package/src/lib/server.d.ts +30 -0
  164. package/src/lib/server.js +95 -0
  165. package/src/lib/server.js.map +1 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/components/marketplace-browse.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 { mdiCheckDecagram } from '@aglyn/shared-data-mdi'\nimport { AppLink, CardDisplay, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { TABLE_PAGE_SIZE_DEFAULT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport {\n Alert,\n Chip,\n Tooltip,\n Grid,\n MenuItem,\n Rating,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport {\n collection,\n doc,\n documentId,\n getDoc,\n limit,\n orderBy,\n query,\n where,\n} from 'firebase/firestore'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport {\n buildRoute,\n type OrgPermissions,\n PLATFORM_BRAND_NAME,\n pluginDocsHelp,\n Route,\n} from '@aglyn/aglyn'\nimport {\n useConsoleHostRoute,\n useFirestore,\n useFirestoreCollection,\n useFirestoreDoc,\n useHostOrgId,\n useUser,\n useScopeTokens,\n} from '@aglyn/tenant-feature-instance'\n/*\n * The MODULE, not the barrel, for the two PURE helpers — the specs that render\n * this grid mock `@aglyn/tenant-feature-instance` wholesale to stage their\n * Firestore hooks, and a query builder imported through that barrel disappears\n * under the mock. Neither of these is a hook.\n */\nimport {\n ceilingedWindow,\n collectionCeiling,\n} from '@aglyn/tenant-feature-instance/hooks/host-collection-queries'\nimport {\n isListingBrowsable,\n isListingDeleted,\n isPrivateListing,\n listingArtifactType,\n listingArtifactLabel,\n resolvePluginInstallState,\n} from '../model/marketplace'\nimport { ListingImage } from './listing-image.component'\n\n// The console route table is shared (AGL-685), so these go through\n// buildRoute rather than being reassembled from a base string — the shape\n// of `/[orgSlug]/hosts/[host]/marketplace/…` is not this plugin's to know.\n\n/**\n * How many install records the shelf reads to decide what it already runs.\n *\n * One number for the five collections that answer that question, because the\n * card reads them together and a reader comparing a plugin's chip to a\n * dataset's should not have to know which window was twice the other. It\n * bounds an install-state LOOKUP, not the shelf: the listings query has its\n * own cap, and the grid pages what survives the filters.\n */\nconst INSTALL_STATE_CEILING = 100\n\nexport interface MarketplaceBrowseProps {\n hostId: string\n /** Signed-in user's org permissions, supplied by the shell (AGL-395). */\n permissions?: Partial<OrgPermissions>\n /**\n * Rendered inside the org-scope `/marketplace` route (AGL-772) rather\n * than a site's marketplace tab. Only affects link targets — the grid\n * still installs through the acting `hostId` until targeting lands\n * (AGL-773) — so detail links resolve to the org route, not a per-site\n * one that is being retired.\n */\n orgScoped?: boolean\n /**\n * The acting org's slug from the URL (AGL-867). When given at org scope,\n * detail links build from it directly instead of the async\n * `hostIndex`→`orgs` resolution, which can return empty and leave the detail\n * page — the only place installs happen now — unreachable from browse.\n */\n orgSlug?: string\n /**\n * Restrict the grid to one publisher's listings (AGL-869), for the org-scope\n * publisher page. Omitted on the main browse, which shows everyone.\n */\n publisherId?: string\n}\n\n/**\n * Marketplace components browse (AGL-44). A read-only catalogue: each card links\n * to the listing's detail page, which is the only place an install happens\n * (AGL-867) — installing from a grid card was too easy and skipped the\n * site-targeting choice. The card still shows install STATE (installed,\n * org-wide) so the shelf reads honestly, but carries no install action.\n */\nexport function MarketplaceBrowse(props: MarketplaceBrowseProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { orgScoped, orgSlug: orgSlugProp, publisherId } = props\n const [handles, setHandles] = useState<Record<string, string>>({})\n // Listings are org-owned (AGL-652), so \"is this mine\" is an org comparison.\n // Resolved from the routing mirror rather than a new prop so the component\n // stays self-contained; hostIndex is signed-in readable.\n const [viewerOrgId, setViewerOrgId] = useState<string | null>(null)\n useEffect(() => {\n let active = true\n void getDoc(doc(firestore, 'hostIndex', hostId))\n .then((snapshot) => {\n if (active) setViewerOrgId((snapshot.get('orgId') as string) ?? null)\n })\n .catch(() => undefined)\n return () => {\n active = false\n }\n }, [firestore, hostId])\n // Card links were built as `/{hostDocId}/marketplace/…`, a shape that has\n // not resolved since AGL-621/622 — every listing and publisher link on\n // this grid 404'd. One shared resolution (AGL-673); null renders plain\n // text rather than a link to nowhere.\n const { orgSlug: resolvedOrgSlug, subdomain } = useConsoleHostRoute(hostId)\n // Prefer the URL-supplied slug (AGL-867) — synchronous and always present at\n // org scope — over the async host resolution, which the per-site route still\n // relies on.\n const orgSlug = orgSlugProp ?? resolvedOrgSlug\n\n // Link targets differ by surface (AGL-772): the org marketplace resolves\n // to the org route (`/[orgSlug]/marketplace/[listingId]`), the per-site tab\n // to the host route. Null → plain text, never a link to nowhere. Publisher\n // pages have no org route yet, so they stay text at org scope for now.\n const listingHref = (listingId: string) =>\n orgScoped\n ? orgSlug\n ? buildRoute(Route.ORG_MARKETPLACE_LISTING, { orgSlug, listingId })\n : undefined\n : orgSlug && subdomain\n ? buildRoute(Route.ORG_MARKETPLACE_LISTING, {\n orgSlug,\n listingId,\n })\n : undefined\n // Storefront links carry the publisher's HANDLE (AGL-1001), the identity\n // shown right beside them on the card. The id remains a valid segment on\n // the page itself, so a publisher whose handle hasn't loaded yet still\n // links somewhere real rather than nowhere.\n const publisherHref = (profileId: string) =>\n orgScoped\n ? // Org-scope publisher storefront (AGL-869): all of one publisher's\n // listings. Needs the URL slug, which is passed in at org scope.\n orgSlug\n ? buildRoute(Route.ORG_MARKETPLACE_PUBLISHER, {\n orgSlug,\n handle: handles[profileId] ?? profileId,\n })\n : undefined\n : orgSlug && subdomain\n ? buildRoute(Route.ORG_MARKETPLACE_PUBLISHER, {\n orgSlug,\n handle: handles[profileId] ?? profileId,\n })\n : undefined\n\n /**\n * NO `where('deletedAt','==',null)` HERE — soft-deleted listings are dropped\n * in `items` below instead (AGL-1196).\n *\n * `deletedAt` flips every time a publisher unpublishes or republishes. A\n * document that stops matching a live query leaves the query target, and the\n * client can cache a `noDocument` tombstone at its own path — which is then\n * served to every reader of that path, including the detail page, which\n * reads `marketplaceListings/{id}` BY ID. So a browse session open across an\n * unpublish could 404 a listing that exists, and republishing would not\n * clear it: a resumed listen only pulls deltas, so an otherwise-unchanged\n * document is never re-sent (AGL-827, AGL-929).\n *\n * Dropping the predicate removes the mechanism rather than healing it: with\n * no `where`, no document can stop matching. The rule permits it —\n * `marketplaceListings` is `allow read: if true`, with no `resource.data`\n * term, so an unconstrained list is not denied. (That is NOT true of the\n * scoped `datasets` query below, which is why this fix does not generalise.)\n *\n * It also fixes a quieter bug: `== null` matches only an EXPLICIT null, and\n * Firestore cannot express \"field is absent\". Every publish path stamps\n * `deletedAt: null` today, so this happens to be exact — but any future\n * write path that omits the field would make a live listing silently\n * invisible. An in-memory falsy check treats absent as live, which is what\n * the dataset and email-template filters in this file already do.\n *\n * The cap counts CANDIDATES, not results — private and unreviewed listings\n * already consume slots and get filtered below, so this is the same class of\n * consumption, nudged up to leave room for soft-deleted rows. Measured on\n * production 2026-08-03: 7 listings, 0 soft-deleted, 0 missing the field.\n *\n * ## The cap DOES take an `orderBy`, and it is not the same decision\n *\n * A `limit` with no ordering is answered in document-id order, and listing\n * ids are random — so the ninety this loaded were an arbitrary ninety, and\n * the client sort below then arranged that sample by date and called it\n * \"Newest\". The window is now the newest ninety, which is what the default\n * sort already claimed it was.\n *\n * `orderBy` carries the drop the `where` above was removed to avoid — it\n * matches only documents that HAVE the field, so a listing without\n * `createdAt` would not be mis-ordered, it would be invisible. That is safe\n * here for a reason the `deletedAt` predicate could not offer: a listing is\n * created by exactly two paths, `publish.ts` and `publish-plugin.ts`, and\n * both stamp `createdAt` in the same `set` that brings the document into\n * existence. Every other writer resolves an EXISTING listing by id and\n * refuses when it is absent, so none of them can produce one without the\n * field.\n *\n * It also cannot reproduce the tombstone (AGL-827/929) that made a mutable\n * `where` unsafe: `createdAt` is written under `existing.empty` and never\n * rewritten, so no document can stop matching mid-session and leave a\n * `noDocument` cached at a path the detail page reads by id.\n */\n const { data: listings } = useFirestoreCollection<any>(\n () =>\n query(\n collection(firestore, 'marketplaceListings'),\n orderBy('createdAt', 'desc'),\n limit(90),\n ),\n [firestore],\n { idField: '$id' },\n )\n /*\n * ## The install-state lookups are ORDERED too, and they are not lists\n *\n * The five queries below (this one, the two pin collections, the org\n * datasets and the site's email templates) answer one question per card:\n * has this listing already been installed here. None of them is rendered —\n * each is folded into a map keyed by listing id — so the fix that suits the\n * shelf above does not suit them: there is no page to walk and no footer to\n * put a control in.\n *\n * What they share with it is the defect. A bare `limit` is answered in\n * DOCUMENT-ID order, so an unnamed window is an arbitrary slice of its\n * collection, and a lookup MISS is indistinguishable from a genuine\n * not-installed: a workspace past such a window is shown \"Add to this site\"\n * beside something it already runs, and the detail page — which reads its\n * own pins by id — disagrees with the grid it was reached from.\n *\n * `collectionCeiling` names the order and asks for one row past the\n * ceiling, which is what turns \"the window may have bitten\" into a fact the\n * shelf can state rather than a silence it cannot.\n */\n const { data: installedRead } = useFirestoreCollection<any>(\n () =>\n collectionCeiling(\n collection(firestore, 'hosts', hostId, 'components'),\n INSTALL_STATE_CEILING,\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const { rows: installedDocs, truncated: installedTruncated } = useMemo(\n () => ceilingedWindow<any>(installedRead, INSTALL_STATE_CEILING),\n [installedRead],\n )\n\n // listingId → installed component doc (deleted installs don't count).\n const installed = useMemo(() => {\n const map: Record<string, any> = {}\n for (const definition of installedDocs ?? []) {\n const listingId = definition?.marketplace?.listingId\n if (listingId && !definition.deletedAt) map[listingId] = definition\n }\n return map\n }, [installedDocs])\n\n // Plugin installs are version PINS, not component snapshots (AGL-656): the\n // `components` map above never holds one, so a plugin already installed —\n // at host or org scope — showed \"Add to this site\". These two pin\n // collections are what the loader honors (a host pin shadows an org pin).\n const orgId = useHostOrgId(hostId)\n const { data: hostPinRead } = useFirestoreCollection<any>(\n () =>\n collectionCeiling(\n collection(firestore, 'hosts', hostId, 'installs'),\n INSTALL_STATE_CEILING,\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const { rows: hostPinDocs, truncated: hostPinsTruncated } = useMemo(\n () => ceilingedWindow<any>(hostPinRead, INSTALL_STATE_CEILING),\n [hostPinRead],\n )\n // Held at null while `useHostOrgId` is in flight, never `orgs/-pending-`\n // (AGL-1440): the AGL-1047 comment seven lines below records the same\n // denial-every-mount shape for `useScopeTokens` — this listen had it too.\n const { data: orgPinRead } = useFirestoreCollection<any>(\n () =>\n orgId\n ? collectionCeiling(\n collection(firestore, 'orgs', orgId, 'installs'),\n INSTALL_STATE_CEILING,\n )\n : null,\n [firestore, orgId],\n { idField: '$id' },\n )\n const { rows: orgPinDocs, truncated: orgPinsTruncated } = useMemo(\n () => ceilingedWindow<any>(orgPinRead, INSTALL_STATE_CEILING),\n [orgPinRead],\n )\n // Scoped (AGL-1044): an unfiltered list is REJECTED for a scoped member,\n // not filtered, so this would error without the constraint.\n //\n // Wait for the member doc before listing (AGL-1047). `useScopeTokens`\n // reports `orgWide: true` while loading, so without `scopeLoaded` a scoped\n // collaborator's first render computes `needsScope: false` and sends an\n // UNFILTERED list that the AGL-1041 rules deny per document. It recovers\n // on the next render, which is what makes it easy to miss: the page looks\n // right and logs a denial every mount.\n const {\n tokens: scopeTokens,\n orgWide: viewerOrgWide,\n loaded: scopeLoaded,\n } = useScopeTokens(orgId ?? undefined)\n const needsScope = Boolean(orgId) && !viewerOrgWide\n // The AGL-657 types land in neither the components collection nor a pin\n // (AGL-789): a dataset schema becomes an org dataset, an email template a\n // draft version. Both installers stamp the source listing, so read those.\n /*\n * `documentId()` rather than a field, for the reason the audience sweep in\n * `campaign-send.ts` gives: Firestore's automatic single-field index for an\n * array member is keyed on the value and the document name, so\n * `array-contains-any` plus `orderBy(__name__)` is served by it. Ordering on\n * anything else would need a composite index per scope shape.\n *\n * The ceiling is the shared one. It is not sized for the dataset\n * collection: it is sized for the number of LISTINGS whose install state a\n * shelf of ninety cards can describe, which is the same question its four\n * neighbours answer, so it is the same number.\n */\n const { data: datasetRead } = useFirestoreCollection<any>(\n () =>\n orgId && scopeLoaded\n ? query(\n collection(firestore, 'orgs', orgId, 'datasets'),\n ...(needsScope\n ? [where('visibleTo', 'array-contains-any', scopeTokens)]\n : []),\n orderBy(documentId()),\n limit(INSTALL_STATE_CEILING + 1),\n )\n : null,\n [firestore, orgId, scopeLoaded, needsScope, scopeTokens],\n // `visibleTo` is MUTABLE — every scope edit rewrites it — so this query\n // can tombstone a dataset the way AGL-827 tombstoned a host. The rule\n // requires the constraint for anyone who is not org-wide, so unlike the\n // listings query above the predicate cannot simply be dropped (AGL-1196).\n { idField: '$id', confirmDisappearances: true },\n )\n const { rows: datasetDocs, truncated: datasetsTruncated } = useMemo(\n () => ceilingedWindow<any>(datasetRead, INSTALL_STATE_CEILING),\n [datasetRead],\n )\n const { data: emailRead } = useFirestoreCollection<any>(\n () =>\n collectionCeiling(\n collection(firestore, 'hosts', hostId, 'emailTemplates'),\n INSTALL_STATE_CEILING,\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const { rows: emailDocs, truncated: emailsTruncated } = useMemo(\n () => ceilingedWindow<any>(emailRead, INSTALL_STATE_CEILING),\n [emailRead],\n )\n // A theme is not a document in a collection — it is a field on the site\n // (AGL-1020) — so its install is read from the host doc rather than from a\n // per-artifact query like the two above.\n const { data: hostDoc } = useFirestoreDoc<any>(\n () => doc(firestore, 'hosts', hostId),\n [firestore, hostId],\n { idField: '$id' },\n )\n\n // listingId → the newest install of it. A schema install deliberately makes\n // a NEW dataset every time, so this is genuinely one-to-many; the highest\n // installed version is what the card should speak for.\n const artifactInstalls = useMemo(() => {\n const map: Record<string, { version: string | null }> = {}\n const note = (listingId: unknown, version: unknown) => {\n const id = listingId ? String(listingId) : ''\n if (!id) return\n const next = version != null ? String(version) : null\n const seen = map[id]?.version\n if (!seen || (next && next > seen)) map[id] = { version: next }\n }\n for (const dataset of datasetDocs ?? []) {\n if (dataset.deletedAt) continue\n note(dataset.source?.listingId, dataset.source?.version)\n }\n for (const template of emailDocs ?? []) {\n if (template.deletedAt) continue\n note(template.installedFrom?.listingId, template.installedFrom?.version)\n }\n note(hostDoc?.themeInstalledFrom?.listingId, hostDoc?.themeInstalledFrom?.version)\n return map\n }, [datasetDocs, emailDocs, hostDoc])\n\n const hostPins = useMemo(() => {\n const map: Record<string, any> = {}\n for (const pin of hostPinDocs ?? []) map[pin.$id] = pin\n return map\n }, [hostPinDocs])\n const orgPins = useMemo(() => {\n const map: Record<string, any> = {}\n for (const pin of orgPinDocs ?? []) map[pin.$id] = pin\n return map\n }, [orgPinDocs])\n\n // Resolve each listing's publisher handle once. `handles` must NOT be a\n // dependency here: the effect writes `handles`, so listing it would make the\n // effect re-run on its own output. During the post-load window, when\n // `listings` arrives while every other subscription on this always-mounted\n // grid is also settling, that self-retrigger adds render+effect cycles to an\n // already dense flurry — enough that a concurrent update elsewhere can trip\n // React's nested-update limit (AGL-785). A ref of already-requested ids\n // dedupes instead, so the effect depends only on `listings`.\n const requestedHandles = useRef<Set<string>>(new Set())\n useEffect(() => {\n const profileIds = [\n ...new Set((listings ?? []).map((listing: any) => listing.profileId)),\n ].filter(\n (profileId) =>\n profileId && !requestedHandles.current.has(String(profileId)),\n )\n if (!profileIds.length) return\n for (const profileId of profileIds)\n requestedHandles.current.add(String(profileId))\n let cancelled = false\n Promise.all(\n profileIds.map(async (profileId) => {\n const snapshot = await getDoc(\n doc(firestore, 'publisherProfiles', String(profileId)),\n ).catch(() => null)\n return [profileId, snapshot?.get('handle') ?? ''] as const\n }),\n ).then((entries) => {\n if (!cancelled) {\n setHandles((prev) => ({\n ...prev,\n ...Object.fromEntries(entries),\n }))\n }\n })\n return () => {\n cancelled = true\n }\n }, [listings, firestore])\n\n // Browse controls (AGL-95): client-side search/filter/sort over the\n // fetched page of listings.\n const [search, setSearch] = useState('')\n const [category, setCategory] = useState<string | null>(null)\n const [sort, setSort] = useState<'newest' | 'installed' | 'rated'>(\n 'newest',\n )\n const categories = useMemo(\n () =>\n [\n ...new Set(\n (listings ?? [])\n .map((listing: any) => listing.category)\n .filter(Boolean),\n ),\n ].sort() as string[],\n [listings],\n )\n const items = useMemo(() => {\n const needle = search.trim().toLowerCase()\n const filtered = (listings ?? []).filter((listing: any) => {\n // Private plugins never appear here, not even for the org that owns\n // them (AGL-968/993). The owner exemption below exists so a publisher\n // can watch their own SUBMISSION move through review — but a private\n // listing is not waiting to be listed, it is deliberately not for the\n // marketplace, and showing it in this grid is the one thing \"private\"\n // promises will not happen. Owners reach theirs from\n // Marketplace › Listings, whose View opens the same detail page\n // installs happen on.\n // Soft-deleted listings used to be excluded by the query itself; they\n // are dropped here now so no mutable field sits in a `where`\n // (AGL-1196). Unconditional — unlike the review gate below, deletion\n // has no owner exemption.\n if (isListingDeleted(listing)) return false\n if (isPrivateListing(listing)) return false\n // Review queue gate (AGL-432): unreviewed/rejected plugin listings\n // stay off the public browse; the owner still sees their own (the\n // detail page shows them the status). UX-level only — the docs are\n // public-readable by design.\n if (!isListingBrowsable(listing) && listing.profileId !== viewerOrgId) {\n return false\n }\n // Publisher page (AGL-869): only this publisher's listings.\n if (publisherId && listing.profileId !== publisherId) return false\n if (category && listing.category !== category) return false\n if (!needle) return true\n return [listing.displayName, listing.description, listing.category]\n .filter(Boolean)\n .some((value: string) => value.toLowerCase().includes(needle))\n })\n return [...filtered].sort((a: any, b: any) => {\n if (sort === 'installed') {\n return (b.installCount ?? 0) - (a.installCount ?? 0)\n }\n if (sort === 'rated') {\n // Unrated listings sort last rather than as zero-stars — a new\n // listing has not been judged badly, it has not been judged.\n const byAverage =\n (b.ratingAverage ?? -1) - (a.ratingAverage ?? -1)\n return byAverage || (b.ratingCount ?? 0) - (a.ratingCount ?? 0)\n }\n return (b.createdAt?.seconds ?? 0) - (a.createdAt?.seconds ?? 0)\n })\n }, [listings, search, category, sort, publisherId, user?.uid])\n\n /*\n * The shelf PAGES (AGL-2501). It used to render every card the window held\n * in one wall, so the only control over how much arrived at once was the\n * cap in the query — a number the reader cannot see and cannot change.\n *\n * Client-side, deliberately, and this is the case `ListPagination`'s\n * `count` prop exists for. The search box, the category chips and the\n * owner/review gates are all resolved in memory, so a server page would be\n * a page of CANDIDATES: a reader filtering to one category would get pages\n * that were mostly empty and a count that meant nothing. Paging what is\n * left after the filter is the only version whose numbers are about what is\n * on screen.\n */\n /**\n * The install-state lookups did not read everything they were asked about.\n *\n * Said on the SHELF rather than on the card that is wrong, because there is\n * no way to know which card that is: a miss and a genuine not-installed are\n * the same absence from the same map. What the reader can act on is that the\n * chips are a floor — anything marked installed is installed, and something\n * unmarked may still be.\n */\n const installStateTruncated =\n installedTruncated ||\n hostPinsTruncated ||\n orgPinsTruncated ||\n datasetsTruncated ||\n emailsTruncated\n\n const [page, setPage] = useState(0)\n const [pageSize, setPageSize] = useState(TABLE_PAGE_SIZE_DEFAULT)\n /*\n * A new query starts at page one. Page four of a category with two pages\n * does not exist, and an out-of-range page renders as an empty grid with\n * nothing saying why — which reads as the filter having broken.\n */\n useEffect(() => {\n setPage(0)\n }, [search, category, sort, pageSize])\n const shown = useMemo(\n () => items.slice(page * pageSize, (page + 1) * pageSize),\n [items, page, pageSize],\n )\n\n return (\n <CardDisplay\n header={'Marketplace components'}\n help={pluginDocsHelp('plugins', { anchor: '#install--upgrade' })}\n contentGutterX\n contentGutterY\n >\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ mb: 2, alignItems: 'center', flexWrap: 'wrap', rowGap: 1 }}\n >\n <TextField\n placeholder=\"Search components…\"\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n size=\"small\"\n sx={{ minWidth: 200 }}\n />\n {categories.map((value) => (\n <Chip\n key={value}\n label={value}\n variant={category === value ? 'filled' : 'outlined'}\n color={category === value ? 'primary' : 'default'}\n onClick={() =>\n setCategory((previous) => (previous === value ? null : value))\n }\n />\n ))}\n <TextField\n value={sort}\n onChange={(event) => setSort(event.target.value as any)}\n size=\"small\"\n select\n sx={{ ml: 'auto', minWidth: 150 }}\n >\n <MenuItem value=\"newest\">{'Newest'}</MenuItem>\n <MenuItem value=\"installed\">{'Most installed'}</MenuItem>\n <MenuItem value=\"rated\">{'Highest rated'}</MenuItem>\n </TextField>\n </Stack>\n {installStateTruncated ? (\n <Alert severity=\"info\" sx={{ mb: 2 }}>\n {`Installed state is resolved from the first ${INSTALL_STATE_CEILING} ` +\n 'install records in this workspace, ordered by id. There are ' +\n 'more, so a card may not be marked installed even though it is. ' +\n 'Open a listing to see what this workspace actually runs.'}\n </Alert>\n ) : null}\n {items.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No marketplace components published yet — publish one of your ' +\n 'reusable components from the Setup page to be the first.'}\n </Typography>\n ) : (\n <>\n <Grid container spacing={2}>\n {shown.map((listing: any) => {\n const artifactType = listingArtifactType(listing)\n const isPlugin = artifactType === 'plugin'\n const pluginState = resolvePluginInstallState(\n listing.latestVersion,\n isPlugin ? hostPins[listing.$id] : null,\n isPlugin ? orgPins[listing.$id] : null,\n )\n const componentInstall = installed[listing.$id]\n // datasetSchema/emailTemplate installs are tracked by the source\n // listing stamped on what they created (AGL-789).\n //\n // `emailStarter` is deliberately absent. Every other type has ONE\n // install per site to point at — a pin, a dataset, a draft on a\n // fixed catalog key, the site's theme — so \"Installed\" names\n // something. A starter installs as a NEW email each time, and a\n // site may keep five of them; there is no single copy for a chip\n // to describe, and marking the listing installed would grey out\n // the button that makes the sixth.\n const artifactInstall =\n artifactType === 'datasetSchema' ||\n artifactType === 'emailTemplate' ||\n artifactType === 'theme'\n ? artifactInstalls[listing.$id]\n : undefined\n const isInstalled = isPlugin\n ? pluginState.scope != null\n : Boolean(componentInstall ?? artifactInstall)\n const installedVersion = isPlugin\n ? pluginState.installedVersion\n : (componentInstall?.marketplace?.version ??\n artifactInstall?.version)\n const priceUsd = Number(listing.priceUsd ?? 0)\n const detailHref = listingHref(listing.$id)\n return (\n <Grid key={listing.$id} size={{ xs: 12, sm: 6, md: 4 }}>\n <Stack\n spacing={1}\n sx={{\n border: 1,\n borderColor: 'divider',\n borderRadius: 1,\n p: 2,\n height: '100%',\n }}\n >\n {/* Resolved, never raw (AGL-1424) — the stored value may be\n a `media:` reference. */}\n <ListingImage\n src={listing.previewImageUrl}\n alt={`${listing.displayName} preview`}\n sx={{\n width: '100%',\n height: 120,\n objectFit: 'cover',\n borderRadius: 1,\n }}\n />\n {/* The name gets the whole line (AGL-1002). Sharing a row\n with the chips meant the title was the only thing that\n gave way when they did not fit — \"Promo Countdown\"\n truncated to \"Promo Coun…\" beside chips with room to\n spare, which inverts what matters on a browse card. */}\n <AppLink\n href={listingHref(listing.$id)}\n color=\"inherit\"\n underline=\"hover\"\n variant=\"subtitle2\"\n >\n {listing.displayName}\n </AppLink>\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', flexWrap: 'wrap', rowGap: 1 }}\n >\n {/* Primary classification, said first (AGL-864). */}\n <Chip\n size=\"small\"\n color=\"primary\"\n label={listingArtifactLabel(listing)}\n />\n {listing.category ? (\n <Chip size=\"small\" label={listing.category} />\n ) : null}\n {/* The reviewed badge belongs HERE most of all\n (AGL-1002): the detail page showed it, but browse is\n where someone is comparing options and deciding whose\n code to run.\n\n TWO CLAIMS, SHOWN SEPARATELY (AGL-1121). This chip\n vouches for the PUBLISHER; the one beside it says\n whether THESE bytes were reviewed. They were a single\n \"Verified\" chip driven by `reviewStatus`, which lives\n on the listing and deliberately survives a version\n bump — so a publisher verified on v1.0.0 could ship\n v1.9.0 containing anything and still be badged as\n though the code had been read.\n\n Both tooltips name `PLATFORM_BRAND_NAME` rather than\n the viewing org's `productName` (AGL-2351). They say\n who did the verifying and the reading — the\n deployment's own review queue, one catalog shared by\n every org — and a white-label agency's brand here would\n claim the agency vetted a publisher it has never heard\n of. */}\n {listing.reviewStatus === 'verified' ? (\n <Tooltip\n title={\n `A human at ${PLATFORM_BRAND_NAME} confirmed who ` +\n 'this publisher is, ' +\n 'and that the listing describes what the code does. ' +\n 'It is a claim about the publisher, not about this ' +\n 'release — it survives a version bump.'\n }\n >\n <Chip\n size=\"small\"\n color=\"info\"\n label=\"Verified publisher\"\n // Carries more weight than the neighbouring\n // classification chips on purpose: it is the only one\n // that says a human vouched for the code, and the\n // rest are just taxonomy. The icon is what makes it\n // findable when scanning a grid rather than reading\n // one card.\n icon={\n <MdiIcon\n path={mdiCheckDecagram.path}\n sx={{ fontSize: 16 }}\n />\n }\n sx={{ fontWeight: 600 }}\n />\n </Tooltip>\n ) : null}\n {/* The bytes on offer, not the person who wrote them.\n Absent on a listing published before this field\n existed, which reads as \"not reviewed\" — the safe\n direction for a claim about code. */}\n {listing.latestVersionReviewState === 'approved' ? (\n <Tooltip\n title={\n `A human at ${PLATFORM_BRAND_NAME} read these exact ` +\n 'bytes — the ' +\n 'version on offer — against a required checklist. ' +\n 'Re-earned per version, so a new release starts ' +\n 'without it. Not a security guarantee: every plugin ' +\n 'runs in the same sandbox either way.'\n }\n >\n <Chip size=\"small\" color=\"success\" label=\"Reviewed\" />\n </Tooltip>\n ) : null}\n {/*\n Price on EVERY card, `Free` included (AGL-2173). The\n chip only rendered above zero, so the four free\n listings the marketplace mockup shows carried no\n price at all — and \"no chip\" is the one reading a\n shopper cannot distinguish from \"we forgot\", on the\n row where the paid listing beside it says $29.\n */}\n <Chip\n size=\"small\"\n color={priceUsd > 0 ? 'primary' : 'success'}\n variant={priceUsd > 0 ? 'filled' : 'outlined'}\n label={priceUsd > 0 ? `$${priceUsd}` : 'Free'}\n />\n </Stack>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`v${listing.latestVersion}`}\n {handles[listing.profileId] ? (\n <>\n {' · by '}\n <AppLink\n href={publisherHref(listing.profileId)}\n color=\"primary\"\n underline=\"hover\"\n >\n {`@${handles[listing.profileId]}`}\n </AppLink>\n </>\n ) : (\n ''\n )}\n {listing.installCount\n ? ` · ${listing.installCount} install${\n listing.installCount === 1 ? '' : 's'\n }`\n : ''}\n </Typography>\n {/*\n Stars, as the mockup draws them (AGL-2173). The count\n stays alongside the average: \"5.0\" from one rating and\n from forty are not the same claim (AGL-655). An unrated\n listing says so rather than rendering nothing — silence\n read as \"no stars\", which is the opposite of \"not yet\n rated\".\n */}\n <Stack\n direction=\"row\"\n spacing={0.5}\n sx={{ alignItems: 'center' }}\n >\n {listing.ratingCount ? (\n <>\n <Rating\n size=\"small\"\n readOnly\n precision={0.1}\n value={Number(listing.ratingAverage ?? 0)}\n />\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${listing.ratingAverage ?? 0} (${listing.ratingCount})`}\n </Typography>\n </>\n ) : (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Not yet rated'}\n </Typography>\n )}\n </Stack>\n {listing.description ? (\n <Typography\n variant=\"body2\"\n color=\"text.secondary\"\n sx={{ flex: 1 }}\n // Four lines, then ellipsis (AGL-1002). Grid rows\n // stretch to their tallest card, so one publisher's\n // essay used to leave every card beside it mostly white\n // space; the full text is one click away.\n //\n // Plain `style`, not `sx`: emotion drops the\n // `-webkit-box` display value on the way through, and\n // without it the other three properties clamp nothing\n // (measured — `display` computed to `flow-root` while\n // line-clamp and box-orient came through fine). Nothing\n // here reads the theme, so there is nothing to lose.\n style={{\n display: '-webkit-box',\n WebkitBoxOrient: 'vertical',\n WebkitLineClamp: 4,\n overflow: 'hidden',\n }}\n >\n {listing.description}\n </Typography>\n ) : (\n <span style={{ flex: 1 }} />\n )}\n {/* Read-only state, then a link to the detail page — the\n only place an install happens (AGL-867). No install/buy\n action lives on the grid. */}\n {/* Install state reads as one statement (AGL-1002): the\n scope belongs with \"Installed\", not up in the chip row\n among the type and category pills, which say what the\n listing IS rather than what this workspace has done\n with it. Org-wide installs apply to every site\n (AGL-656). */}\n {isInstalled ? (\n <Stack\n direction=\"row\"\n spacing={0.75}\n sx={{ alignItems: 'center', flexWrap: 'wrap' }}\n >\n <Typography variant=\"caption\" color=\"success.main\">\n {`Installed${\n installedVersion ? ` (v${installedVersion})` : ''\n }`}\n </Typography>\n {isPlugin && pluginState.scope === 'org' ? (\n <Chip\n size=\"small\"\n variant=\"outlined\"\n label={\n pluginState.shadowed\n ? 'Org-wide (shadowed)'\n : 'Org-wide'\n }\n />\n ) : null}\n </Stack>\n ) : null}\n <AppLink\n componentVariant=\"button\"\n size=\"small\"\n variant=\"outlined\"\n color=\"primary\"\n href={detailHref ?? ''}\n disabled={!detailHref}\n >\n {priceUsd > 0 && !isInstalled\n ? `View details · $${priceUsd}`\n : 'View details'}\n </AppLink>\n </Stack>\n </Grid>\n )\n })}\n </Grid>\n {/* `count` is the FILTERED total, which this list genuinely knows —\n it is the array it just sliced. Handing MUI the window's size\n instead would count listings the reader has filtered out. */}\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={shown.length}\n count={items.length}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n </>\n )}\n </CardDisplay>\n )\n}\nMarketplaceBrowse.displayName = 'MarketplaceBrowse'\n\nexport default MarketplaceBrowse\n"],"names":["mdiCheckDecagram","AppLink","CardDisplay","MdiIcon","ListPagination","TABLE_PAGE_SIZE_DEFAULT","Alert","Chip","Tooltip","Grid","MenuItem","Rating","Stack","TextField","Typography","collection","doc","documentId","getDoc","limit","orderBy","query","where","useEffect","useMemo","useRef","useState","buildRoute","PLATFORM_BRAND_NAME","pluginDocsHelp","Route","useConsoleHostRoute","useFirestore","useFirestoreCollection","useFirestoreDoc","useHostOrgId","useUser","useScopeTokens","ceilingedWindow","collectionCeiling","isListingBrowsable","isListingDeleted","isPrivateListing","listingArtifactType","listingArtifactLabel","resolvePluginInstallState","ListingImage","INSTALL_STATE_CEILING","MarketplaceBrowse","props","hostId","firestore","data","user","orgScoped","orgSlug","orgSlugProp","publisherId","handles","setHandles","viewerOrgId","setViewerOrgId","active","then","snapshot","get","catch","undefined","resolvedOrgSlug","subdomain","listingHref","listingId","ORG_MARKETPLACE_LISTING","publisherHref","profileId","ORG_MARKETPLACE_PUBLISHER","handle","listings","idField","installedRead","rows","installedDocs","truncated","installedTruncated","installed","map","definition","marketplace","deletedAt","orgId","hostPinRead","hostPinDocs","hostPinsTruncated","orgPinRead","orgPinDocs","orgPinsTruncated","tokens","scopeTokens","orgWide","viewerOrgWide","loaded","scopeLoaded","needsScope","Boolean","datasetRead","confirmDisappearances","datasetDocs","datasetsTruncated","emailRead","emailDocs","emailsTruncated","hostDoc","artifactInstalls","note","version","id","String","next","seen","dataset","source","template","installedFrom","themeInstalledFrom","hostPins","pin","$id","orgPins","requestedHandles","Set","profileIds","listing","filter","current","has","length","add","cancelled","Promise","all","entries","prev","Object","fromEntries","search","setSearch","category","setCategory","sort","setSort","categories","items","needle","trim","toLowerCase","filtered","displayName","description","some","value","includes","a","b","installCount","byAverage","ratingAverage","ratingCount","createdAt","seconds","uid","installStateTruncated","page","setPage","pageSize","setPageSize","shown","slice","header","help","anchor","contentGutterX","contentGutterY","direction","spacing","sx","mb","alignItems","flexWrap","rowGap","placeholder","onChange","event","target","size","minWidth","label","variant","color","onClick","previous","select","ml","severity","container","componentInstall","artifactType","isPlugin","pluginState","latestVersion","artifactInstall","isInstalled","scope","installedVersion","priceUsd","Number","detailHref","xs","sm","md","border","borderColor","borderRadius","p","height","src","previewImageUrl","alt","width","objectFit","href","underline","reviewStatus","title","icon","path","fontSize","fontWeight","latestVersionReviewState","readOnly","precision","flex","style","display","WebkitBoxOrient","WebkitLineClamp","overflow","span","shadowed","componentVariant","disabled","rowCount","count","onPageChange","onPageSizeChange"],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export interface PluginSiteSetPanelProps {
2
+ /** Acting site — org pins still resolve their org through one. */
3
+ hostId: string;
4
+ listingId: string;
5
+ hosts?: ReadonlyArray<{
6
+ id: string;
7
+ label: string;
8
+ }>;
9
+ }
10
+ /**
11
+ * The `pluginSiteSet` slot (AGL-1007): the site-set control as something the
12
+ * console can drop onto the installation detail page without importing this
13
+ * plugin.
14
+ *
15
+ * It reads its own pins rather than taking them as props, because the page
16
+ * that hosts it has no other use for them — unlike the listing page, which
17
+ * also gates its pre-install picker and primary button on the same state and
18
+ * therefore keeps ownership of it there.
19
+ */
20
+ export declare function PluginSiteSetPanel(props: PluginSiteSetPanelProps): import("react").JSX.Element;
21
+ export declare namespace PluginSiteSetPanel {
22
+ var displayName: string;
23
+ }
24
+ export default PluginSiteSetPanel;
@@ -0,0 +1,137 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ 'use client';
17
+ import { jsx as _jsx } from "react/jsx-runtime";
18
+ import { Typography } from "@mui/material";
19
+ import { doc, getDoc } from "firebase/firestore";
20
+ import { useEffect, useMemo, useState } from "react";
21
+ import { useFirestore, useFirestoreDoc, useHostOrgId } from "@aglyn/tenant-feature-instance";
22
+ import { offeredPluginVersion } from "@aglyn/aglyn";
23
+ import { resolveOrgInstallSummary } from "../model/marketplace.js";
24
+ import { useMarketplaceActions } from "../hooks/use-marketplace-actions.js";
25
+ import PluginSiteSet from "./plugin-site-set.component.js";
26
+ /**
27
+ * The `pluginSiteSet` slot (AGL-1007): the site-set control as something the
28
+ * console can drop onto the installation detail page without importing this
29
+ * plugin.
30
+ *
31
+ * It reads its own pins rather than taking them as props, because the page
32
+ * that hosts it has no other use for them — unlike the listing page, which
33
+ * also gates its pre-install picker and primary button on the same state and
34
+ * therefore keeps ownership of it there.
35
+ */ export function PluginSiteSetPanel(props) {
36
+ var _ref, _offeredPluginVersion;
37
+ const { hostId, listingId, hosts } = props;
38
+ const firestore = useFirestore();
39
+ const orgId = useHostOrgId(hostId);
40
+ const { installPlan, uninstall } = useMarketplaceActions(hostId);
41
+ // Held at null while `useHostOrgId` is in flight, never `orgs/-pending-`
42
+ // (AGL-1440): installs are member-gated, so the sentinel was a
43
+ // guaranteed-denied listen on every mount of this panel.
44
+ const { data: orgPin } = useFirestoreDoc(()=>orgId && listingId ? doc(firestore, 'orgs', orgId, 'installs', listingId) : null, [
45
+ firestore,
46
+ orgId,
47
+ listingId
48
+ ]);
49
+ const [sitePins, setSitePins] = useState({});
50
+ const [pinsNonce, setPinsNonce] = useState(0);
51
+ const hostIdsKey = (hosts != null ? hosts : []).map((host)=>host.id).join('|');
52
+ useEffect(()=>{
53
+ if (!listingId || !(hosts == null ? void 0 : hosts.length)) return;
54
+ let active = true;
55
+ void Promise.all(hosts.map(async (host)=>{
56
+ const snapshot = await getDoc(doc(firestore, 'hosts', host.id, 'installs', listingId));
57
+ return [
58
+ host.id,
59
+ snapshot.exists() ? snapshot.data() : null
60
+ ];
61
+ })).then((entries)=>{
62
+ if (active) setSitePins(Object.fromEntries(entries));
63
+ }).catch(()=>undefined);
64
+ return ()=>{
65
+ active = false;
66
+ };
67
+ // eslint-disable-next-line react-hooks/exhaustive-deps
68
+ }, [
69
+ firestore,
70
+ listingId,
71
+ hostIdsKey,
72
+ pinsNonce
73
+ ]);
74
+ // The listing, for the version actually on offer (AGL-1017). This used to
75
+ // pass a PIN's own version as `latestVersion`, which is by definition the
76
+ // version that pin is already at — so no site ever looked stale and the
77
+ // update control never appeared on this page at all.
78
+ const { data: listing } = useFirestoreDoc(()=>doc(firestore, 'marketplaceListings', listingId || '-missing-'), [
79
+ firestore,
80
+ listingId
81
+ ]);
82
+ // The kill switch (AGL-2368). This panel offers an UPDATE, so it is asking
83
+ // "what installs today" and had only the review-verdict mirror to ask —
84
+ // which nothing cleared on revoke, so it offered a version the install
85
+ // route refuses. Listing-scoped and public, one read for the panel.
86
+ const { data: revocation } = useFirestoreDoc(()=>listingId ? doc(firestore, 'revocations', listingId) : null, [
87
+ firestore,
88
+ listingId
89
+ ]);
90
+ const orgInstall = useMemo(()=>resolveOrgInstallSummary(hosts != null ? hosts : [], sitePins, orgPin != null ? orgPin : null), [
91
+ hosts,
92
+ sitePins,
93
+ orgPin
94
+ ]);
95
+ // The listing doc the actions need. The pin carries the denormalised copy
96
+ // the actions actually read (`$id`, `displayName`), so this page does not
97
+ // pay for a second read of the listing itself.
98
+ const anyPin = (_ref = orgPin != null ? orgPin : Object.values(sitePins).find(Boolean)) != null ? _ref : null;
99
+ const actionListing = useMemo(()=>{
100
+ var _ref, _ref1, _ref2;
101
+ return {
102
+ $id: listingId,
103
+ displayName: (_ref = (_ref1 = listing == null ? void 0 : listing.displayName) != null ? _ref1 : anyPin == null ? void 0 : anyPin.displayName) != null ? _ref : listingId,
104
+ artifactType: 'plugin',
105
+ latestVersion: (_ref2 = listing == null ? void 0 : listing.latestVersion) != null ? _ref2 : anyPin == null ? void 0 : anyPin.version
106
+ };
107
+ }, [
108
+ listingId,
109
+ listing == null ? void 0 : listing.displayName,
110
+ listing == null ? void 0 : listing.latestVersion,
111
+ anyPin == null ? void 0 : anyPin.displayName,
112
+ anyPin == null ? void 0 : anyPin.version
113
+ ]);
114
+ if (!orgInstall.installedAnywhere) {
115
+ return /*#__PURE__*/ _jsx(Typography, {
116
+ variant: "body2",
117
+ color: "text.secondary",
118
+ children: 'Not installed on any site in this organization.'
119
+ });
120
+ }
121
+ return /*#__PURE__*/ _jsx(PluginSiteSet, {
122
+ listing: actionListing,
123
+ hosts: hosts != null ? hosts : [],
124
+ orgInstall: orgInstall,
125
+ // The newest INSTALLABLE version (AGL-1016/2368) — never `latestVersion`,
126
+ // and never the review verdict alone, or the page offers an update the
127
+ // install route refuses.
128
+ latestVersion: (_offeredPluginVersion = offeredPluginVersion(listing, revocation)) != null ? _offeredPluginVersion : undefined,
129
+ installPlan: installPlan,
130
+ uninstall: uninstall,
131
+ onChanged: ()=>setPinsNonce((current)=>current + 1)
132
+ });
133
+ }
134
+ PluginSiteSetPanel.displayName = 'PluginSiteSetPanel';
135
+ export default PluginSiteSetPanel;
136
+
137
+ //# sourceMappingURL=plugin-site-set-panel.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/components/plugin-site-set-panel.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 { Typography } from '@mui/material'\nimport { doc, getDoc } from 'firebase/firestore'\nimport { useEffect, useMemo, useState } from 'react'\nimport {\n useFirestore,\n useFirestoreDoc,\n useHostOrgId,\n} from '@aglyn/tenant-feature-instance'\nimport { offeredPluginVersion } from '@aglyn/aglyn'\nimport { resolveOrgInstallSummary } from '../model/marketplace'\nimport { useMarketplaceActions } from '../hooks/use-marketplace-actions'\nimport PluginSiteSet from './plugin-site-set.component'\n\nexport interface PluginSiteSetPanelProps {\n /** Acting site — org pins still resolve their org through one. */\n hostId: string\n listingId: string\n hosts?: ReadonlyArray<{ id: string; label: string }>\n}\n\n/**\n * The `pluginSiteSet` slot (AGL-1007): the site-set control as something the\n * console can drop onto the installation detail page without importing this\n * plugin.\n *\n * It reads its own pins rather than taking them as props, because the page\n * that hosts it has no other use for them — unlike the listing page, which\n * also gates its pre-install picker and primary button on the same state and\n * therefore keeps ownership of it there.\n */\nexport function PluginSiteSetPanel(props: PluginSiteSetPanelProps) {\n const { hostId, listingId, hosts } = props\n const firestore = useFirestore()\n const orgId = useHostOrgId(hostId)\n const { installPlan, uninstall } = useMarketplaceActions(hostId)\n\n // Held at null while `useHostOrgId` is in flight, never `orgs/-pending-`\n // (AGL-1440): installs are member-gated, so the sentinel was a\n // guaranteed-denied listen on every mount of this panel.\n const { data: orgPin } = useFirestoreDoc<any>(\n () =>\n orgId && listingId\n ? doc(firestore, 'orgs', orgId, 'installs', listingId)\n : null,\n [firestore, orgId, listingId],\n )\n const [sitePins, setSitePins] = useState<Record<string, any>>({})\n const [pinsNonce, setPinsNonce] = useState(0)\n const hostIdsKey = (hosts ?? []).map((host) => host.id).join('|')\n useEffect(() => {\n if (!listingId || !hosts?.length) return\n let active = true\n void Promise.all(\n hosts.map(async (host) => {\n const snapshot = await getDoc(\n doc(firestore, 'hosts', host.id, 'installs', listingId),\n )\n return [host.id, snapshot.exists() ? snapshot.data() : null] as const\n }),\n )\n .then((entries) => {\n if (active) setSitePins(Object.fromEntries(entries))\n })\n .catch(() => undefined)\n return () => {\n active = false\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [firestore, listingId, hostIdsKey, pinsNonce])\n\n // The listing, for the version actually on offer (AGL-1017). This used to\n // pass a PIN's own version as `latestVersion`, which is by definition the\n // version that pin is already at — so no site ever looked stale and the\n // update control never appeared on this page at all.\n const { data: listing } = useFirestoreDoc<any>(\n () => doc(firestore, 'marketplaceListings', listingId || '-missing-'),\n [firestore, listingId],\n )\n\n // The kill switch (AGL-2368). This panel offers an UPDATE, so it is asking\n // \"what installs today\" and had only the review-verdict mirror to ask —\n // which nothing cleared on revoke, so it offered a version the install\n // route refuses. Listing-scoped and public, one read for the panel.\n const { data: revocation } = useFirestoreDoc<any>(\n () =>\n listingId ? doc(firestore, 'revocations', listingId) : null,\n [firestore, listingId],\n )\n\n const orgInstall = useMemo(\n () => resolveOrgInstallSummary(hosts ?? [], sitePins, orgPin ?? null),\n [hosts, sitePins, orgPin],\n )\n // The listing doc the actions need. The pin carries the denormalised copy\n // the actions actually read (`$id`, `displayName`), so this page does not\n // pay for a second read of the listing itself.\n const anyPin = orgPin ?? Object.values(sitePins).find(Boolean) ?? null\n const actionListing = useMemo(\n () => ({\n $id: listingId,\n displayName: listing?.displayName ?? anyPin?.displayName ?? listingId,\n artifactType: 'plugin',\n latestVersion: listing?.latestVersion ?? anyPin?.version,\n }),\n [listingId, listing?.displayName, listing?.latestVersion, anyPin?.displayName, anyPin?.version],\n )\n\n if (!orgInstall.installedAnywhere) {\n return (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Not installed on any site in this organization.'}\n </Typography>\n )\n }\n\n return (\n <PluginSiteSet\n listing={actionListing}\n hosts={hosts ?? []}\n orgInstall={orgInstall}\n // The newest INSTALLABLE version (AGL-1016/2368) — never `latestVersion`,\n // and never the review verdict alone, or the page offers an update the\n // install route refuses.\n latestVersion={offeredPluginVersion(listing, revocation) ?? undefined}\n installPlan={installPlan}\n uninstall={uninstall}\n onChanged={() => setPinsNonce((current) => current + 1)}\n />\n )\n}\n\nPluginSiteSetPanel.displayName = 'PluginSiteSetPanel'\n\nexport default PluginSiteSetPanel\n"],"names":["Typography","doc","getDoc","useEffect","useMemo","useState","useFirestore","useFirestoreDoc","useHostOrgId","offeredPluginVersion","resolveOrgInstallSummary","useMarketplaceActions","PluginSiteSet","PluginSiteSetPanel","props","orgPin","hostId","listingId","hosts","firestore","orgId","installPlan","uninstall","data","sitePins","setSitePins","pinsNonce","setPinsNonce","hostIdsKey","map","host","id","join","length","active","Promise","all","snapshot","exists","then","entries","Object","fromEntries","catch","undefined","listing","revocation","orgInstall","anyPin","values","find","Boolean","actionListing","$id","displayName","artifactType","latestVersion","version","installedAnywhere","variant","color","onChanged","current"],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import { type OrgInstallSummary } from '../model/marketplace';
2
+ export interface PluginSiteSetProps {
3
+ /** The listing/installation these pins belong to. */
4
+ listing: any;
5
+ /** Every site in the org, in display order. */
6
+ hosts: ReadonlyArray<{
7
+ id: string;
8
+ label: string;
9
+ }>;
10
+ /** The resolved picture — see `resolveOrgInstallSummary`. */
11
+ orgInstall: OrgInstallSummary;
12
+ /** Version the pins should be at; a site behind this can be updated. */
13
+ latestVersion?: string | number | null;
14
+ /** Install the given concrete steps (org pin and/or named host pins). */
15
+ installPlan: (listing: any, steps: ReadonlyArray<{
16
+ scope: 'org' | 'host';
17
+ hostId?: string;
18
+ }>, options?: {
19
+ intent?: 'install' | 'update';
20
+ version?: string | number | null;
21
+ }) => Promise<unknown>;
22
+ /** Drop a pin — org-wide, or from one named site. */
23
+ uninstall: (listing: any, scope?: 'org' | 'host', hostId?: string) => Promise<unknown>;
24
+ /** Re-read the pins after a write; the caller owns that state. */
25
+ onChanged: () => void;
26
+ }
27
+ /**
28
+ * The set of sites a plugin is installed on, and the controls to change it
29
+ * (AGL-997), extracted from the listing page so the installation detail page
30
+ * (AGL-1007) shows the same thing rather than a second implementation that
31
+ * drifts.
32
+ *
33
+ * An org-scope install is a SET, not a boolean: an org pin covers every site
34
+ * including ones created later, host pins name specific sites, and a host
35
+ * pin on top of an org pin shadows it for that one site. All three facts
36
+ * have to survive into the UI, which is why this takes the whole summary
37
+ * rather than a scope.
38
+ */
39
+ export declare function PluginSiteSet(props: PluginSiteSetProps): import("react").JSX.Element;
40
+ export declare namespace PluginSiteSet {
41
+ var displayName: string;
42
+ }
43
+ export default PluginSiteSet;