@aglyn/plugins-marketplace 1.0.0-beta.149 → 1.0.0-beta.151

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 (92) hide show
  1. package/package.json +15 -11
  2. package/src/lib/components/edit-listing-dialog.component.d.ts +20 -0
  3. package/src/lib/components/edit-listing-dialog.component.js +51 -0
  4. package/src/lib/components/edit-listing-dialog.component.js.map +1 -0
  5. package/src/lib/components/listing-detail-editor.component.d.ts +24 -0
  6. package/src/lib/components/listing-detail-editor.component.js +457 -0
  7. package/src/lib/components/listing-detail-editor.component.js.map +1 -0
  8. package/src/lib/components/listing-detail-page.component.d.ts +28 -0
  9. package/src/lib/components/listing-detail-page.component.js +151 -0
  10. package/src/lib/components/listing-detail-page.component.js.map +1 -0
  11. package/src/lib/components/listing-review-status.component.d.ts +67 -0
  12. package/src/lib/components/listing-review-status.component.js +301 -0
  13. package/src/lib/components/listing-review-status.component.js.map +1 -0
  14. package/src/lib/components/listing-verification-request.component.d.ts +40 -0
  15. package/src/lib/components/listing-verification-request.component.js +170 -0
  16. package/src/lib/components/listing-verification-request.component.js.map +1 -0
  17. package/src/lib/components/marketplace-hub.component.d.ts +43 -0
  18. package/src/lib/components/marketplace-hub.component.js +244 -0
  19. package/src/lib/components/marketplace-hub.component.js.map +1 -0
  20. package/src/lib/components/marketplace-reports-page.component.d.ts +23 -0
  21. package/src/lib/components/marketplace-reports-page.component.js +324 -0
  22. package/src/lib/components/marketplace-reports-page.component.js.map +1 -0
  23. package/src/lib/components/org-licences-panel.component.d.ts +73 -0
  24. package/src/lib/components/org-licences-panel.component.js +318 -0
  25. package/src/lib/components/org-licences-panel.component.js.map +1 -0
  26. package/src/lib/components/org-publish-panel.component.d.ts +45 -0
  27. package/src/lib/components/org-publish-panel.component.js +402 -0
  28. package/src/lib/components/org-publish-panel.component.js.map +1 -0
  29. package/src/lib/components/org-seller-panel.component.d.ts +28 -0
  30. package/src/lib/components/org-seller-panel.component.js +956 -0
  31. package/src/lib/components/org-seller-panel.component.js.map +1 -0
  32. package/src/lib/components/plugin-review-detail.component.d.ts +16 -0
  33. package/src/lib/components/plugin-review-detail.component.js +1438 -0
  34. package/src/lib/components/plugin-review-detail.component.js.map +1 -0
  35. package/src/lib/components/plugin-reviews-queue.component.d.ts +13 -0
  36. package/src/lib/components/plugin-reviews-queue.component.js +385 -0
  37. package/src/lib/components/plugin-reviews-queue.component.js.map +1 -0
  38. package/src/lib/components/plugin-reviews-surface.component.d.ts +19 -0
  39. package/src/lib/components/plugin-reviews-surface.component.js +46 -0
  40. package/src/lib/components/plugin-reviews-surface.component.js.map +1 -0
  41. package/src/lib/components/publish-plugin-form.component.d.ts +61 -0
  42. package/src/lib/components/publish-plugin-form.component.js +857 -0
  43. package/src/lib/components/publish-plugin-form.component.js.map +1 -0
  44. package/src/lib/components/publish-plugin-page.component.d.ts +29 -0
  45. package/src/lib/components/publish-plugin-page.component.js +92 -0
  46. package/src/lib/components/publish-plugin-page.component.js.map +1 -0
  47. package/src/lib/components/publisher-profile-page.component.d.ts +13 -0
  48. package/src/lib/components/publisher-profile-page.component.js +261 -0
  49. package/src/lib/components/publisher-profile-page.component.js.map +1 -0
  50. package/src/lib/constants/listing-media.d.ts +28 -0
  51. package/src/lib/constants/listing-media.js +28 -0
  52. package/src/lib/constants/listing-media.js.map +1 -0
  53. package/src/lib/model/listing-social-card.d.ts +94 -0
  54. package/src/lib/model/listing-social-card.js +177 -0
  55. package/src/lib/model/listing-social-card.js.map +1 -0
  56. package/src/lib/model/marketplace-paths.d.ts +54 -0
  57. package/src/lib/model/marketplace-paths.js +59 -0
  58. package/src/lib/model/marketplace-paths.js.map +1 -0
  59. package/src/lib/model/payout-readiness.d.ts +81 -0
  60. package/src/lib/model/payout-readiness.js +52 -0
  61. package/src/lib/model/payout-readiness.js.map +1 -0
  62. package/src/lib/model/plugin-rejection-categories.d.ts +66 -0
  63. package/src/lib/model/plugin-rejection-categories.js +124 -0
  64. package/src/lib/model/plugin-rejection-categories.js.map +1 -0
  65. package/src/lib/model/plugin-review-checklist.d.ts +62 -0
  66. package/src/lib/model/plugin-review-checklist.js +131 -0
  67. package/src/lib/model/plugin-review-checklist.js.map +1 -0
  68. package/src/lib/model/plugin-review-status.d.ts +104 -0
  69. package/src/lib/model/plugin-review-status.js +126 -0
  70. package/src/lib/model/plugin-review-status.js.map +1 -0
  71. package/src/lib/model/seller-ledger-totals.d.ts +100 -0
  72. package/src/lib/model/seller-ledger-totals.js +96 -0
  73. package/src/lib/model/seller-ledger-totals.js.map +1 -0
  74. package/src/lib/plugin.js +170 -40
  75. package/src/lib/plugin.js.map +1 -1
  76. package/src/lib/server/admin-reports.d.ts +18 -0
  77. package/src/lib/server/admin-reports.js +280 -0
  78. package/src/lib/server/admin-reports.js.map +1 -0
  79. package/src/lib/server/admin-reviews.d.ts +18 -0
  80. package/src/lib/server/admin-reviews.js +1341 -0
  81. package/src/lib/server/admin-reviews.js.map +1 -0
  82. package/src/lib/server/listing-social-card.server.d.ts +24 -0
  83. package/src/lib/server/listing-social-card.server.js +65 -0
  84. package/src/lib/server/listing-social-card.server.js.map +1 -0
  85. package/src/lib/server/publisher-review-email.d.ts +54 -0
  86. package/src/lib/server/publisher-review-email.js +85 -0
  87. package/src/lib/server/publisher-review-email.js.map +1 -0
  88. package/src/lib/server/revoke-cache-drop.d.ts +59 -0
  89. package/src/lib/server/revoke-cache-drop.js +74 -0
  90. package/src/lib/server/revoke-cache-drop.js.map +1 -0
  91. package/src/lib/server.js +47 -0
  92. package/src/lib/server.js.map +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/components/listing-review-status.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 { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport {\n Alert,\n Chip,\n Divider,\n Stack,\n Typography,\n} from '@mui/material'\nimport { useEffect, useState } from 'react'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n attestationLabels,\n PUBLISHER_ATTESTATION,\n requiredAttestationIds,\n} from '@aglyn/aglyn/app-utils/publisher-attestation'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\n\nexport interface PublisherVersion {\n version: string\n publishedAtMs: number | null\n sha256: string\n reviewState: string\n grandfathered: boolean\n signed: boolean\n changelog: string\n rejectionReason: string\n /**\n * The platform kill switch, per version (AGL-2328). Independent of\n * `reviewState` and NOT a substitute for it: a version is normally\n * approved first and revoked afterwards, so both are true at once and the\n * card has to say both.\n */\n revoked?: boolean\n /**\n * Reconciled against the listing totals server-side (AGL-1418), so this\n * card and the listing's own Version history print the same number for the\n * same version instead of two answers to one question.\n */\n activeInstalls: number\n installCount?: number\n attestation: string[]\n}\n\ninterface PublisherVersionsPayload {\n versions: PublisherVersion[]\n latestApprovedVersion: string\n latestVersion: string\n /** Listing-scoped — one reason covers every revoked version (AGL-2328). */\n revocationReason?: string\n revokedAtMs?: number | null\n}\n\n/**\n * A review state said in words (AGL-1079).\n *\n * `grandfathered` is checked FIRST and deliberately: those versions were\n * published before review was per-version (AGL-966), so they carry no\n * verdict at all. Calling them \"pending\" would promise a reviewer who is\n * never coming, and calling them \"approved\" would claim a judgement nobody\n * made.\n */\nfunction reviewLabel(entry: PublisherVersion): {\n label: string\n color: 'default' | 'success' | 'warning' | 'error' | 'info'\n detail: string\n} {\n if (entry.grandfathered) {\n return {\n label: 'Published before review',\n color: 'default',\n detail:\n 'Published before versions were reviewed individually. It installs, ' +\n 'but no reviewer ever signed off on these bytes.',\n }\n }\n switch (entry.reviewState) {\n case 'approved':\n return {\n label: 'Approved',\n color: 'success',\n detail: 'A reviewer approved these exact bytes.',\n }\n case 'rejected':\n return {\n label: 'Rejected',\n color: 'error',\n detail:\n 'Not installable. Fix what the reason says and publish a new ' +\n 'version — these bytes cannot be edited.',\n }\n case 'pending':\n return {\n label: 'In review',\n color: 'warning',\n detail:\n 'Waiting for a reviewer. Nobody else can install it — but you ' +\n 'can, on your own sites, which is how you test a version before ' +\n 'it is approved.',\n }\n default:\n return {\n label: 'Unknown',\n color: 'default',\n detail: 'No review state was recorded for this version.',\n }\n }\n}\n\nexport interface ListingReviewStatusProps {\n listingId: string\n /** Only a plugin listing has a review-per-version story to tell. */\n isPlugin: boolean\n}\n\n/**\n * What review is doing with a publisher's versions (AGL-1079).\n *\n * AGL-966's guarantee — a new version waits in the queue while the\n * previously approved one keeps installing — has been true and invisible\n * since the day it shipped. A publisher got an email on the outcome and,\n * in between, a listing row identical to the one they had before they\n * published. This is the page that answers what they submitted, what state\n * it is in, which version their customers are actually on, and why one was\n * sent back.\n *\n * Reads through the publisher-scoped versions endpoint rather than\n * Firestore: `pluginVersions` is server-only, because the docs carry\n * publish internals a client has no business holding.\n */\nexport function ListingReviewStatus(props: ListingReviewStatusProps) {\n const { listingId, isPlugin } = props\n const { data: user } = useUser()\n const [payload, setPayload] = useState<PublisherVersionsPayload | null>(null)\n const [failed, setFailed] = useState(false)\n\n useEffect(() => {\n let cancelled = false\n if (!listingId || !isPlugin) return undefined\n void (async () => {\n try {\n const response = await authorizedFetch(\n user,\n `/api/marketplace/listing-versions?scope=publisher&listingId=${encodeURIComponent(\n listingId,\n )}`,\n )\n if (!response.ok) {\n if (!cancelled) setFailed(true)\n return\n }\n const data = (await response.json()) as PublisherVersionsPayload\n if (!cancelled) setPayload(data)\n } catch {\n if (!cancelled) setFailed(true)\n }\n })()\n return () => {\n cancelled = true\n }\n }, [listingId, isPlugin, user])\n\n if (!isPlugin) return null\n if (failed) return null\n // Nothing rather than an empty shell: this card is only worth space once\n // it has something to say, and a skeleton that never fills is worse than\n // a card that appears.\n if (!payload) return null\n\n const { versions, latestApprovedVersion, latestVersion } = payload\n const revocationReason = String(payload.revocationReason ?? '').trim()\n const revokedAtMs = payload.revokedAtMs ?? null\n const inReview = versions.filter(\n (entry) => entry.reviewState === 'pending' && !entry.grandfathered,\n )\n /**\n * The version the card names as installing today, revoked (AGL-2328).\n *\n * `latestApprovedVersion` is a REVIEW verdict and the kill switch is not\n * — nothing about revoking a version clears it — so the headline below\n * went on saying \"is approved and is what installs today\" about a version\n * the installer refuses with a 409. That is the sentence a publisher\n * reads before opening a support ticket about installs that stopped\n * working, so it is checked before any of the review arms.\n */\n const installsTodayRevoked = Boolean(\n latestApprovedVersion &&\n versions.some(\n (entry) => entry.version === latestApprovedVersion && entry.revoked,\n ),\n )\n const behind =\n latestApprovedVersion &&\n latestVersion &&\n latestApprovedVersion !== latestVersion\n\n return (\n <CardDisplay\n header={'Review status'}\n help={pluginDocsHelp('publisherHandbook', {\n anchor: '#review-what-happens-after-you-publish',\n excerpt:\n 'What state each version is in, which one installs today, and why ' +\n 'anything was sent back.',\n })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n {/* The AGL-966 guarantee, in a sentence. It has been true since the\n day it shipped and nobody has ever read it. */}\n {installsTodayRevoked ? (\n <Alert severity=\"error\">\n {`v${latestApprovedVersion} was disabled by the platform. It is ` +\n 'still your latest approved version, but nobody can install ' +\n 'it and sites already running it render a placeholder. ' +\n 'Publishing a new version is what clears this — these bytes ' +\n 'cannot be re-enabled by editing them.'}\n </Alert>\n ) : behind && inReview.length ? (\n <Alert severity=\"info\">\n {`v${latestVersion} is in review. New installs get ` +\n `v${latestApprovedVersion} until it is approved, and anyone ` +\n 'already running it stays where they are.'}\n </Alert>\n ) : behind ? (\n <Alert severity=\"warning\">\n {`v${latestVersion} is not approved, so new installs get ` +\n `v${latestApprovedVersion}.`}\n </Alert>\n ) : latestApprovedVersion ? (\n <Alert severity=\"success\">\n {`v${latestApprovedVersion} is approved and is what installs today.`}\n </Alert>\n ) : (\n <Alert severity=\"info\">\n {'Nothing is approved yet, so nobody else can install this. A ' +\n 'reviewer sees your submission in the queue. You can still ' +\n 'install it on your own sites to test it — that is the only ' +\n 'way these bytes run today.'}\n </Alert>\n )}\n\n {/* Stated rather than implied (AGL-1079). Both halves are things a\n publisher currently has to infer from behaviour. */}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'While a version is in review you can still edit this listing — ' +\n 'name, description, README, screenshots and links — and it will ' +\n 'not send anything back to the queue, because approval is about ' +\n 'the bundle’s bytes. The bytes themselves are never edited: ' +\n 'changing them means publishing a new version.'}\n </Typography>\n\n <Divider />\n\n <Stack spacing={2}>\n {versions.map((entry) => {\n const state = reviewLabel(entry)\n const claimed = entry.attestation.length\n // Which set applied to THIS version: the first one published is\n // not asked for a changelog, so measuring every version against\n // the update set would report the first as permanently short.\n const expected = requiredAttestationIds(\n entry.version !== versions[versions.length - 1]?.version,\n ).length\n return (\n <Stack key={entry.version} spacing={0.5}>\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', flexWrap: 'wrap' }}\n >\n <Typography variant=\"subtitle2\">{`v${entry.version}`}</Typography>\n <Chip size=\"small\" color={state.color} label={state.label} />\n {/* Beside the review chip, never instead of it: a version\n is normally approved and THEN revoked, and collapsing\n the two would erase the verdict the publisher earned. */}\n {entry.revoked ? (\n <Chip size=\"small\" color=\"error\" label=\"Disabled\" />\n ) : null}\n {/* Not on a revoked row — it does not install today, and\n the whole defect here was a card that said it did. */}\n {entry.version === latestApprovedVersion && !entry.revoked ? (\n <Chip\n size=\"small\"\n variant=\"outlined\"\n label=\"Installs today\"\n />\n ) : null}\n {entry.signed ? (\n <Chip\n size=\"small\"\n variant=\"outlined\"\n label=\"Realm-trusted\"\n />\n ) : null}\n {entry.activeInstalls ? (\n <Chip\n size=\"small\"\n variant=\"outlined\"\n color={\n state.label === 'Approved' ? undefined : 'warning'\n }\n label={`${entry.activeInstalls} on this version`}\n />\n ) : null}\n </Stack>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {state.detail}\n {entry.publishedAtMs\n ? ` Published ${new Date(\n entry.publishedAtMs,\n ).toLocaleDateString()}.`\n : ''}\n </Typography>\n {/* An unreviewed version that is nonetheless RUNNING\n somewhere. The publisher-testing carve-out in\n `install-plugin` is deliberate — you cannot test a\n version you cannot install — but it is invisible, and a\n card that said \"nothing installs it yet\" over a live\n install was worse than saying nothing. */}\n {entry.activeInstalls &&\n entry.reviewState !== 'approved' &&\n !entry.grandfathered ? (\n <Alert severity=\"warning\" sx={{ mt: 0.5 }}>\n {/* \"Installs\", not \"sites\" (AGL-1418). An org-wide\n install counts once and covers every site in that\n organization, so this used to report a five-site\n exposure as one site — understating the very risk the\n alert exists to raise. */}\n {`${entry.activeInstalls} of your installs ${\n entry.activeInstalls === 1 ? 'is' : 'are'\n } running this version, which no reviewer has approved. ` +\n 'Only your own organization can install it — but an ' +\n 'org-wide install covers every site in it, so if any ' +\n 'of them is live, unreviewed code is serving real ' +\n 'visitors.'}\n </Alert>\n ) : null}\n {/* In place, on the version it belongs to — it only ever\n reached an email before. */}\n {entry.rejectionReason ? (\n <Alert severity=\"error\" sx={{ mt: 0.5 }}>\n {entry.rejectionReason}\n </Alert>\n ) : null}\n {/* The kill switch, said to the publisher it happened to\n (AGL-2328). The consumer whose site broke was told the\n reason first; the publisher who caused it was told\n nothing at all, on the one page that exists to tell them\n what state their versions are in. */}\n {entry.revoked ? (\n <Alert severity=\"error\" sx={{ mt: 0.5 }}>\n {'The platform disabled this version. Nobody can install ' +\n 'it, and sites already running it render a placeholder.'}\n {/* Only when one was recorded. A revocation written\n before the reason was required says so, rather than\n showing an empty quotation that reads as \"no reason\n given\". */}\n {revocationReason ? (\n <Typography variant=\"body2\" sx={{ mt: 1 }}>\n {`Reason: ${revocationReason}`}\n </Typography>\n ) : null}\n {revokedAtMs ? (\n <Typography\n variant=\"caption\"\n sx={{ display: 'block', mt: 0.5 }}\n >\n {`Disabled ${new Date(\n revokedAtMs,\n ).toLocaleDateString()}`}\n </Typography>\n ) : null}\n </Alert>\n ) : null}\n {entry.changelog ? (\n <Typography variant=\"caption\">\n {entry.changelog}\n </Typography>\n ) : null}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {claimed\n ? `You confirmed ${claimed}/${expected}: ` +\n attestationLabels(entry.attestation)\n .map((label) => label.replace(/\\s+/g, ' '))\n .join('; ')\n : `Nothing was confirmed for these bytes — published ` +\n 'before the pre-publish checklist existed, or ' +\n 'republished since.'}\n </Typography>\n {entry.sha256 ? (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ fontFamily: 'monospace' }}\n >\n {`sha256 ${entry.sha256.slice(0, 12)}…`}\n </Typography>\n ) : null}\n </Stack>\n )\n })}\n {versions.length ? null : (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'No versions published yet.'}\n </Typography>\n )}\n </Stack>\n </Stack>\n </CardDisplay>\n )\n}\n\nListingReviewStatus.displayName = 'ListingReviewStatus'\n\nexport default ListingReviewStatus\n\n/** Exported for the spec: the words each state is said in. */\nexport { reviewLabel, PUBLISHER_ATTESTATION }\n"],"names":["CardDisplay","Alert","Chip","Divider","Stack","Typography","useEffect","useState","useUser","attestationLabels","PUBLISHER_ATTESTATION","requiredAttestationIds","authorizedFetch","pluginDocsHelp","reviewLabel","entry","grandfathered","label","color","detail","reviewState","ListingReviewStatus","props","payload","listingId","isPlugin","data","user","setPayload","failed","setFailed","cancelled","undefined","response","encodeURIComponent","ok","json","versions","latestApprovedVersion","latestVersion","revocationReason","String","trim","revokedAtMs","inReview","filter","installsTodayRevoked","Boolean","some","version","revoked","behind","header","help","anchor","excerpt","contentGutterX","contentGutterY","spacing","severity","length","variant","map","state","claimed","attestation","expected","direction","sx","alignItems","flexWrap","size","signed","activeInstalls","publishedAtMs","Date","toLocaleDateString","mt","rejectionReason","display","changelog","replace","join","sha256","fontFamily","slice","displayName"],"mappings":"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"}
@@ -0,0 +1,40 @@
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
+ */
17
+ import { type VerifiableListing } from '@aglyn/aglyn/app-utils/marketplace-verification';
18
+ export interface ListingVerificationRequestProps {
19
+ listingId: string;
20
+ listing: VerifiableListing | null | undefined;
21
+ /** The org the viewer is acting as. */
22
+ viewerOrgId: string | null | undefined;
23
+ user: unknown;
24
+ /** Verified is a plugin-listing claim; other artifact types never ask. */
25
+ isPlugin: boolean;
26
+ }
27
+ /**
28
+ * The publisher's door to the Verified badge (AGL-1217).
29
+ *
30
+ * Before this there was none: verification was reviewer-initiated only, so a
31
+ * publisher who wanted the badge had no affordance and no way to learn that
32
+ * asking was even possible.
33
+ *
34
+ * Eligibility comes from `verificationRequestBlock` — the same function the
35
+ * server enforces. That sharing is deliberate: a rule the button knows and
36
+ * the server does not is decoration, and a rule the server knows and the
37
+ * button does not is a button that fails with no explanation.
38
+ */
39
+ export declare function ListingVerificationRequest(props: ListingVerificationRequestProps): import("react").JSX.Element;
40
+ export default ListingVerificationRequest;
@@ -0,0 +1,170 @@
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, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
18
+ import { CardDisplay } from "@aglyn/shared-ui-jsx";
19
+ import { Alert, Button, Stack, Typography } from "@mui/material";
20
+ import { useState } from "react";
21
+ import { timestampMs, VERIFICATION_DECLINE_COOLDOWN_DAYS, verificationRequestBlock } from "@aglyn/aglyn/app-utils/marketplace-verification";
22
+ import { PLATFORM_BRAND_NAME } from "@aglyn/aglyn/app-utils/platform-brand";
23
+ import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
24
+ import { pluginDocsHelp } from "@aglyn/aglyn";
25
+ /** What the badge actually claims, said once, in the place someone asks for
26
+ * it — so the request is informed rather than a guess at what it buys.
27
+ *
28
+ * `PLATFORM_BRAND_NAME` and NOT the org's resolved `productName` (AGL-2351).
29
+ * The sentence names WHO DID THE VERIFYING, and that is whoever operates this
30
+ * deployment's marketplace — the reviewers who read the listing. Substituting
31
+ * the viewing org's brand would make a white-label agency's console assert
32
+ * that the agency vetted this publisher, which nobody at the agency did. The
33
+ * marketplace is a platform-relationship surface: one catalog, one review
34
+ * queue, shared by every org on the deployment. On a self-host install the
35
+ * operator's own staff really are the humans in this sentence, and the env var
36
+ * renames them with the rest of the product.
37
+ */ const WHAT_VERIFIED_CLAIMS = `Verified says a human at ${PLATFORM_BRAND_NAME} confirmed who you are, ` + 'and that this listing describes what the code does. It is a claim about ' + 'you as a publisher, so it survives a version bump. It is not a security ' + 'guarantee, and it does not review the bytes of any particular release — ' + 'that is what Reviewed means, and it is earned per version.';
38
+ /**
39
+ * The publisher's door to the Verified badge (AGL-1217).
40
+ *
41
+ * Before this there was none: verification was reviewer-initiated only, so a
42
+ * publisher who wanted the badge had no affordance and no way to learn that
43
+ * asking was even possible.
44
+ *
45
+ * Eligibility comes from `verificationRequestBlock` — the same function the
46
+ * server enforces. That sharing is deliberate: a rule the button knows and
47
+ * the server does not is decoration, and a rule the server knows and the
48
+ * button does not is a button that fails with no explanation.
49
+ */ export function ListingVerificationRequest(props) {
50
+ const { listingId, listing, viewerOrgId, user, isPlugin } = props;
51
+ const [busy, setBusy] = useState(false);
52
+ const [error, setError] = useState(null);
53
+ // Optimistically reflects the action just taken, so the card does not sit
54
+ // unchanged while the listing snapshot catches up.
55
+ const [justDid, setJustDid] = useState(null);
56
+ const request = listing == null ? void 0 : listing.verificationRequest;
57
+ const state = justDid != null ? justDid : request == null ? void 0 : request.state;
58
+ const block = verificationRequestBlock({
59
+ listing,
60
+ viewerOrgId,
61
+ nowMs: Date.now()
62
+ });
63
+ const post = async (action)=>{
64
+ setBusy(true);
65
+ setError(null);
66
+ try {
67
+ const response = await authorizedFetch(user, '/api/marketplace/verification-request', {
68
+ method: 'POST',
69
+ headers: {
70
+ 'Content-Type': 'application/json'
71
+ },
72
+ body: JSON.stringify({
73
+ listingId,
74
+ action
75
+ })
76
+ });
77
+ const data = await response.json().catch(()=>({}));
78
+ if (!response.ok) {
79
+ var _data_error;
80
+ // The server's own words. It re-checks the same policy, so when it
81
+ // refuses it knows something this component's snapshot did not.
82
+ setError((_data_error = data.error) != null ? _data_error : 'That did not work. Try again.');
83
+ return;
84
+ }
85
+ setJustDid(action === 'request' ? 'pending' : 'withdrawn');
86
+ } catch (unused) {
87
+ setError('That did not work. Try again.');
88
+ } finally{
89
+ setBusy(false);
90
+ }
91
+ };
92
+ if (!isPlugin) return null;
93
+ // Not the publisher, or already carrying the badge: nothing to offer. The
94
+ // badge itself is rendered on the listing body, so saying so here would be
95
+ // repeating it in a card about asking.
96
+ if (block === 'not-publisher' || block === 'already-verified') return null;
97
+ // A listing still in the queue has a review card above already explaining
98
+ // where it stands. A second card saying "and you cannot ask for a badge
99
+ // yet" adds noise to a publisher who has not asked for anything.
100
+ if (block === 'not-listed' && state !== 'pending') return null;
101
+ const declinedAt = timestampMs(request == null ? void 0 : request.decidedAt);
102
+ const canAskAgain = declinedAt === null ? null : new Date(declinedAt + VERIFICATION_DECLINE_COOLDOWN_DAYS * 86400000).toLocaleDateString();
103
+ return /*#__PURE__*/ _jsx(CardDisplay, {
104
+ header: 'Verified publisher',
105
+ help: pluginDocsHelp('publisherHandbook', {
106
+ anchor: '#asking-to-be-verified',
107
+ excerpt: 'When you can ask, what happens while it waits, and what a decline ' + 'does and does not mean.'
108
+ }),
109
+ children: /*#__PURE__*/ _jsxs(Stack, {
110
+ spacing: 2,
111
+ children: [
112
+ /*#__PURE__*/ _jsx(Typography, {
113
+ variant: "body2",
114
+ color: "text.secondary",
115
+ children: WHAT_VERIFIED_CLAIMS
116
+ }),
117
+ state === 'pending' ? /*#__PURE__*/ _jsxs(_Fragment, {
118
+ children: [
119
+ /*#__PURE__*/ _jsx(Alert, {
120
+ severity: "info",
121
+ children: 'Your request is with the review team. Your plugin stays ' + 'listed and installable while they look.'
122
+ }),
123
+ /*#__PURE__*/ _jsx(Stack, {
124
+ direction: "row",
125
+ children: /*#__PURE__*/ _jsx(Button, {
126
+ variant: "outlined",
127
+ color: "inherit",
128
+ disabled: busy,
129
+ onClick: ()=>void post('withdraw'),
130
+ children: 'Withdraw request'
131
+ })
132
+ })
133
+ ]
134
+ }) : block === 'cooling-down' ? /*#__PURE__*/ _jsx(Alert, {
135
+ severity: "warning",
136
+ children: /*#__PURE__*/ _jsxs(Stack, {
137
+ spacing: 1,
138
+ children: [
139
+ /*#__PURE__*/ _jsx(Typography, {
140
+ variant: "body2",
141
+ children: (request == null ? void 0 : request.declineReason) ? `Declined: ${request.declineReason}` : 'A previous request was declined.'
142
+ }),
143
+ /*#__PURE__*/ _jsx(Typography, {
144
+ variant: "caption",
145
+ color: "text.secondary",
146
+ children: canAskAgain ? `You can ask again from ${canAskAgain}.` : `You can ask again ${VERIFICATION_DECLINE_COOLDOWN_DAYS} days after the decision.`
147
+ })
148
+ ]
149
+ })
150
+ }) : /*#__PURE__*/ _jsx(Stack, {
151
+ direction: "row",
152
+ children: /*#__PURE__*/ _jsx(Button, {
153
+ variant: "contained",
154
+ color: "primary",
155
+ disabled: busy || block !== null,
156
+ onClick: ()=>void post('request'),
157
+ children: 'Request verification'
158
+ })
159
+ }),
160
+ error ? /*#__PURE__*/ _jsx(Alert, {
161
+ severity: "error",
162
+ children: error
163
+ }) : null
164
+ ]
165
+ })
166
+ });
167
+ }
168
+ export default ListingVerificationRequest;
169
+
170
+ //# sourceMappingURL=listing-verification-request.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/components/listing-verification-request.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\n'use client'\n\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { Alert, Button, Stack, Typography } from '@mui/material'\nimport { useState } from 'react'\nimport {\n timestampMs,\n VERIFICATION_DECLINE_COOLDOWN_DAYS,\n verificationRequestBlock,\n type VerifiableListing,\n} from '@aglyn/aglyn/app-utils/marketplace-verification'\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn/app-utils/platform-brand'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\n\nexport interface ListingVerificationRequestProps {\n listingId: string\n listing: VerifiableListing | null | undefined\n /** The org the viewer is acting as. */\n viewerOrgId: string | null | undefined\n user: unknown\n /** Verified is a plugin-listing claim; other artifact types never ask. */\n isPlugin: boolean\n}\n\n/** What the badge actually claims, said once, in the place someone asks for\n * it — so the request is informed rather than a guess at what it buys.\n *\n * `PLATFORM_BRAND_NAME` and NOT the org's resolved `productName` (AGL-2351).\n * The sentence names WHO DID THE VERIFYING, and that is whoever operates this\n * deployment's marketplace — the reviewers who read the listing. Substituting\n * the viewing org's brand would make a white-label agency's console assert\n * that the agency vetted this publisher, which nobody at the agency did. The\n * marketplace is a platform-relationship surface: one catalog, one review\n * queue, shared by every org on the deployment. On a self-host install the\n * operator's own staff really are the humans in this sentence, and the env var\n * renames them with the rest of the product.\n */\nconst WHAT_VERIFIED_CLAIMS =\n `Verified says a human at ${PLATFORM_BRAND_NAME} confirmed who you are, ` +\n 'and that this listing describes what the code does. It is a claim about ' +\n 'you as a publisher, so it survives a version bump. It is not a security ' +\n 'guarantee, and it does not review the bytes of any particular release — ' +\n 'that is what Reviewed means, and it is earned per version.'\n\n/**\n * The publisher's door to the Verified badge (AGL-1217).\n *\n * Before this there was none: verification was reviewer-initiated only, so a\n * publisher who wanted the badge had no affordance and no way to learn that\n * asking was even possible.\n *\n * Eligibility comes from `verificationRequestBlock` — the same function the\n * server enforces. That sharing is deliberate: a rule the button knows and\n * the server does not is decoration, and a rule the server knows and the\n * button does not is a button that fails with no explanation.\n */\nexport function ListingVerificationRequest(\n props: ListingVerificationRequestProps,\n) {\n const { listingId, listing, viewerOrgId, user, isPlugin } = props\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState<string | null>(null)\n // Optimistically reflects the action just taken, so the card does not sit\n // unchanged while the listing snapshot catches up.\n const [justDid, setJustDid] = useState<'pending' | 'withdrawn' | null>(null)\n\n const request = listing?.verificationRequest\n const state = justDid ?? request?.state\n const block = verificationRequestBlock({\n listing,\n viewerOrgId,\n nowMs: Date.now(),\n })\n\n const post = async (action: 'request' | 'withdraw') => {\n setBusy(true)\n setError(null)\n try {\n const response = await authorizedFetch(\n user,\n '/api/marketplace/verification-request',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ listingId, action }),\n },\n )\n const data = (await response.json().catch(() => ({}))) as {\n error?: string\n }\n if (!response.ok) {\n // The server's own words. It re-checks the same policy, so when it\n // refuses it knows something this component's snapshot did not.\n setError(data.error ?? 'That did not work. Try again.')\n return\n }\n setJustDid(action === 'request' ? 'pending' : 'withdrawn')\n } catch {\n setError('That did not work. Try again.')\n } finally {\n setBusy(false)\n }\n }\n\n if (!isPlugin) return null\n // Not the publisher, or already carrying the badge: nothing to offer. The\n // badge itself is rendered on the listing body, so saying so here would be\n // repeating it in a card about asking.\n if (block === 'not-publisher' || block === 'already-verified') return null\n // A listing still in the queue has a review card above already explaining\n // where it stands. A second card saying \"and you cannot ask for a badge\n // yet\" adds noise to a publisher who has not asked for anything.\n if (block === 'not-listed' && state !== 'pending') return null\n\n const declinedAt = timestampMs(request?.decidedAt)\n const canAskAgain =\n declinedAt === null\n ? null\n : new Date(\n declinedAt + VERIFICATION_DECLINE_COOLDOWN_DAYS * 86_400_000,\n ).toLocaleDateString()\n\n return (\n <CardDisplay\n header={'Verified publisher'}\n help={pluginDocsHelp('publisherHandbook', {\n anchor: '#asking-to-be-verified',\n excerpt:\n 'When you can ask, what happens while it waits, and what a decline ' +\n 'does and does not mean.',\n })}\n >\n <Stack spacing={2}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {WHAT_VERIFIED_CLAIMS}\n </Typography>\n\n {state === 'pending' ? (\n <>\n <Alert severity=\"info\">\n {'Your request is with the review team. Your plugin stays ' +\n 'listed and installable while they look.'}\n </Alert>\n <Stack direction=\"row\">\n <Button\n variant=\"outlined\"\n color=\"inherit\"\n disabled={busy}\n onClick={() => void post('withdraw')}\n >\n {'Withdraw request'}\n </Button>\n </Stack>\n </>\n ) : block === 'cooling-down' ? (\n <Alert severity=\"warning\">\n <Stack spacing={1}>\n <Typography variant=\"body2\">\n {request?.declineReason\n ? `Declined: ${request.declineReason}`\n : 'A previous request was declined.'}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {canAskAgain\n ? `You can ask again from ${canAskAgain}.`\n : `You can ask again ${VERIFICATION_DECLINE_COOLDOWN_DAYS} days after the decision.`}\n </Typography>\n </Stack>\n </Alert>\n ) : (\n <Stack direction=\"row\">\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={busy || block !== null}\n onClick={() => void post('request')}\n >\n {'Request verification'}\n </Button>\n </Stack>\n )}\n\n {error ? <Alert severity=\"error\">{error}</Alert> : null}\n </Stack>\n </CardDisplay>\n )\n}\n\nexport default ListingVerificationRequest\n"],"names":["CardDisplay","Alert","Button","Stack","Typography","useState","timestampMs","VERIFICATION_DECLINE_COOLDOWN_DAYS","verificationRequestBlock","PLATFORM_BRAND_NAME","authorizedFetch","pluginDocsHelp","WHAT_VERIFIED_CLAIMS","ListingVerificationRequest","props","listingId","listing","viewerOrgId","user","isPlugin","busy","setBusy","error","setError","justDid","setJustDid","request","verificationRequest","state","block","nowMs","Date","now","post","action","response","method","headers","body","JSON","stringify","data","json","catch","ok","declinedAt","decidedAt","canAskAgain","toLocaleDateString","header","help","anchor","excerpt","spacing","variant","color","severity","direction","disabled","onClick","declineReason"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;AAEA,SAASA,WAAW,QAAQ,uBAAsB;AAClD,SAASC,KAAK,EAAEC,MAAM,EAAEC,KAAK,EAAEC,UAAU,QAAQ,gBAAe;AAChE,SAASC,QAAQ,QAAQ,QAAO;AAChC,SACEC,WAAW,EACXC,kCAAkC,EAClCC,wBAAwB,QAEnB,kDAAiD;AACxD,SAASC,mBAAmB,QAAQ,wCAAuC;AAC3E,SAASC,eAAe,QAAQ,2CAA0C;AAC1E,SAASC,cAAc,QAAQ,eAAc;AAY7C;;;;;;;;;;;;CAYC,GACD,MAAMC,uBACJ,CAAC,yBAAyB,EAAEH,oBAAoB,wBAAwB,CAAC,GACzE,6EACA,6EACA,6EACA;AAEF;;;;;;;;;;;CAWC,GACD,OAAO,SAASI,2BACdC,KAAsC;IAEtC,MAAM,EAAEC,SAAS,EAAEC,OAAO,EAAEC,WAAW,EAAEC,IAAI,EAAEC,QAAQ,EAAE,GAAGL;IAC5D,MAAM,CAACM,MAAMC,QAAQ,GAAGhB,SAAS;IACjC,MAAM,CAACiB,OAAOC,SAAS,GAAGlB,SAAwB;IAClD,0EAA0E;IAC1E,mDAAmD;IACnD,MAAM,CAACmB,SAASC,WAAW,GAAGpB,SAAyC;IAEvE,MAAMqB,UAAUV,2BAAAA,QAASW,mBAAmB;IAC5C,MAAMC,QAAQJ,kBAAAA,UAAWE,2BAAAA,QAASE,KAAK;IACvC,MAAMC,QAAQrB,yBAAyB;QACrCQ;QACAC;QACAa,OAAOC,KAAKC,GAAG;IACjB;IAEA,MAAMC,OAAO,OAAOC;QAClBb,QAAQ;QACRE,SAAS;QACT,IAAI;YACF,MAAMY,WAAW,MAAMzB,gBACrBQ,MACA,yCACA;gBACEkB,QAAQ;gBACRC,SAAS;oBAAE,gBAAgB;gBAAmB;gBAC9CC,MAAMC,KAAKC,SAAS,CAAC;oBAAEzB;oBAAWmB;gBAAO;YAC3C;YAEF,MAAMO,OAAQ,MAAMN,SAASO,IAAI,GAAGC,KAAK,CAAC,IAAO,CAAA,CAAC,CAAA;YAGlD,IAAI,CAACR,SAASS,EAAE,EAAE;oBAGPH;gBAFT,mEAAmE;gBACnE,gEAAgE;gBAChElB,UAASkB,cAAAA,KAAKnB,KAAK,YAAVmB,cAAc;gBACvB;YACF;YACAhB,WAAWS,WAAW,YAAY,YAAY;QAChD,EAAE,eAAM;YACNX,SAAS;QACX,SAAU;YACRF,QAAQ;QACV;IACF;IAEA,IAAI,CAACF,UAAU,OAAO;IACtB,0EAA0E;IAC1E,2EAA2E;IAC3E,uCAAuC;IACvC,IAAIU,UAAU,mBAAmBA,UAAU,oBAAoB,OAAO;IACtE,0EAA0E;IAC1E,wEAAwE;IACxE,iEAAiE;IACjE,IAAIA,UAAU,gBAAgBD,UAAU,WAAW,OAAO;IAE1D,MAAMiB,aAAavC,YAAYoB,2BAAAA,QAASoB,SAAS;IACjD,MAAMC,cACJF,eAAe,OACX,OACA,IAAId,KACFc,aAAatC,qCAAqC,UAClDyC,kBAAkB;IAE1B,qBACE,KAAChD;QACCiD,QAAQ;QACRC,MAAMvC,eAAe,qBAAqB;YACxCwC,QAAQ;YACRC,SACE,uEACA;QACJ;kBAEA,cAAA,MAACjD;YAAMkD,SAAS;;8BACd,KAACjD;oBAAWkD,SAAQ;oBAAQC,OAAM;8BAC/B3C;;gBAGFgB,UAAU,0BACT;;sCACE,KAAC3B;4BAAMuD,UAAS;sCACb,6DACC;;sCAEJ,KAACrD;4BAAMsD,WAAU;sCACf,cAAA,KAACvD;gCACCoD,SAAQ;gCACRC,OAAM;gCACNG,UAAUtC;gCACVuC,SAAS,IAAM,KAAK1B,KAAK;0CAExB;;;;qBAILJ,UAAU,+BACZ,KAAC5B;oBAAMuD,UAAS;8BACd,cAAA,MAACrD;wBAAMkD,SAAS;;0CACd,KAACjD;gCAAWkD,SAAQ;0CACjB5B,CAAAA,2BAAAA,QAASkC,aAAa,IACnB,CAAC,UAAU,EAAElC,QAAQkC,aAAa,EAAE,GACpC;;0CAEN,KAACxD;gCAAWkD,SAAQ;gCAAUC,OAAM;0CACjCR,cACG,CAAC,uBAAuB,EAAEA,YAAY,CAAC,CAAC,GACxC,CAAC,kBAAkB,EAAExC,mCAAmC,yBAAyB,CAAC;;;;mCAK5F,KAACJ;oBAAMsD,WAAU;8BACf,cAAA,KAACvD;wBACCoD,SAAQ;wBACRC,OAAM;wBACNG,UAAUtC,QAAQS,UAAU;wBAC5B8B,SAAS,IAAM,KAAK1B,KAAK;kCAExB;;;gBAKNX,sBAAQ,KAACrB;oBAAMuD,UAAS;8BAASlC;qBAAiB;;;;AAI3D;AAEA,eAAeT,2BAA0B"}
@@ -0,0 +1,43 @@
1
+ import type { ConsolePluginPageProps } from '@aglyn/aglyn';
2
+ /**
3
+ * The Marketplace hub (AGL-772/2501), as a plugin surface (AGL-3080).
4
+ *
5
+ * ## What moved, and what did not
6
+ *
7
+ * Every section of this hub — browse, installed, licences, and the four
8
+ * seller cards — was a hand-written console route under
9
+ * `app/(app)/[orgSlug]/marketplace/`, ten route files whose whole job was to
10
+ * assemble chrome around a body that already belonged to this plugin. The
11
+ * shell's generic org plugin route does that assembly for every other
12
+ * surface, from one declaration. So the declaration is now in `plugin.ts`
13
+ * and this is the body.
14
+ *
15
+ * NO URL CHANGED. `ConsoleNavItem.ownsSubtree` is what makes that true: a
16
+ * segment naming a declared section resolves as a section, and anything else
17
+ * beneath the surface is handed over as `segments` for this page to read. So
18
+ * `/{org}/marketplace/browse` is still the browse tab and
19
+ * `/{org}/marketplace/{listingId}` is still a listing — the same addresses,
20
+ * served by a declaration instead of by seventeen files.
21
+ *
22
+ * ## The gates are the shell's now, and that is the point
23
+ *
24
+ * The seller sections read the organization's REVENUE and its payout account,
25
+ * and the old sections layout gated them itself — above the pages rather than
26
+ * in the rail, because a route can be typed whether or not a tab was ever
27
+ * offered. That is exactly what `ConsoleNavSection.permission` does, applied
28
+ * by the same shell that hides the tab, so a hidden tab and a refused deep
29
+ * link are one verdict instead of two copies of one. The permission map fails
30
+ * OPEN while it loads, and the shell holds the whole page on it — so this
31
+ * component never has to know that.
32
+ *
33
+ * Likewise `release_marketplace`: the sections layout wrapped itself in a
34
+ * `FeatureGate` because the flag also feeds the plugin LOADER, and a hub that
35
+ * went on rendering with its widgets empty made turning the feature off its
36
+ * own broken state (AGL-2019). The shell applies the same flag through the
37
+ * nav item's `navTabId`, around the same page.
38
+ */
39
+ export declare function MarketplaceHub(props: ConsolePluginPageProps): import("react").JSX.Element;
40
+ export declare namespace MarketplaceHub {
41
+ var displayName: string;
42
+ }
43
+ export default MarketplaceHub;
@@ -0,0 +1,244 @@
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, jsxs as _jsxs } from "react/jsx-runtime";
18
+ import { HubSections } from "@aglyn/shared-ui-next/components/hub-tabs";
19
+ import { AppLink } from "@aglyn/shared-ui-jsx";
20
+ import { Alert, Button, MenuItem, Stack, TextField, Typography } from "@mui/material";
21
+ import { useMemo, useState } from "react";
22
+ import HostPluginsCard from "./host-plugins-card.component.js";
23
+ import ListingDetailPage from "./listing-detail-page.component.js";
24
+ import MarketplaceBrowse from "./marketplace-browse.component.js";
25
+ import MarketplacePaymentsNotice from "./marketplace-payments-notice.component.js";
26
+ import OrgLicencesPanel from "./org-licences-panel.component.js";
27
+ import OrgPublishPanel from "./org-publish-panel.component.js";
28
+ import OrgSellerPanel from "./org-seller-panel.component.js";
29
+ import PublisherProfilePage from "./publisher-profile-page.component.js";
30
+ import PublishPluginPage from "./publish-plugin-page.component.js";
31
+ /**
32
+ * The Marketplace hub (AGL-772/2501), as a plugin surface (AGL-3080).
33
+ *
34
+ * ## What moved, and what did not
35
+ *
36
+ * Every section of this hub — browse, installed, licences, and the four
37
+ * seller cards — was a hand-written console route under
38
+ * `app/(app)/[orgSlug]/marketplace/`, ten route files whose whole job was to
39
+ * assemble chrome around a body that already belonged to this plugin. The
40
+ * shell's generic org plugin route does that assembly for every other
41
+ * surface, from one declaration. So the declaration is now in `plugin.ts`
42
+ * and this is the body.
43
+ *
44
+ * NO URL CHANGED. `ConsoleNavItem.ownsSubtree` is what makes that true: a
45
+ * segment naming a declared section resolves as a section, and anything else
46
+ * beneath the surface is handed over as `segments` for this page to read. So
47
+ * `/{org}/marketplace/browse` is still the browse tab and
48
+ * `/{org}/marketplace/{listingId}` is still a listing — the same addresses,
49
+ * served by a declaration instead of by seventeen files.
50
+ *
51
+ * ## The gates are the shell's now, and that is the point
52
+ *
53
+ * The seller sections read the organization's REVENUE and its payout account,
54
+ * and the old sections layout gated them itself — above the pages rather than
55
+ * in the rail, because a route can be typed whether or not a tab was ever
56
+ * offered. That is exactly what `ConsoleNavSection.permission` does, applied
57
+ * by the same shell that hides the tab, so a hidden tab and a refused deep
58
+ * link are one verdict instead of two copies of one. The permission map fails
59
+ * OPEN while it loads, and the shell holds the whole page on it — so this
60
+ * component never has to know that.
61
+ *
62
+ * Likewise `release_marketplace`: the sections layout wrapped itself in a
63
+ * `FeatureGate` because the flag also feeds the plugin LOADER, and a hub that
64
+ * went on rendering with its widgets empty made turning the feature off its
65
+ * own broken state (AGL-2019). The shell applies the same flag through the
66
+ * nav item's `navTabId`, around the same page.
67
+ */ export function MarketplaceHub(props) {
68
+ var _ref, _ref1;
69
+ var _hostList_;
70
+ const { orgMount, basePath, sections, section, segments, permissions, viewerOrgs, org } = props;
71
+ const orgId = (_ref = orgMount == null ? void 0 : orgMount.orgId) != null ? _ref : null;
72
+ const orgSlug = (_ref1 = orgMount == null ? void 0 : orgMount.orgSlug) != null ? _ref1 : '';
73
+ const hostList = useMemo(()=>{
74
+ var _ref;
75
+ return ((_ref = orgMount == null ? void 0 : orgMount.hosts) != null ? _ref : []).map((host)=>({
76
+ id: host.id,
77
+ label: host.name
78
+ }));
79
+ }, [
80
+ orgMount == null ? void 0 : orgMount.hosts
81
+ ]);
82
+ /**
83
+ * The site an install acts through. Installs pin to a site and pins are
84
+ * validated against host membership, so every section that installs needs
85
+ * the same answer — which is why it is chosen once here rather than per
86
+ * section.
87
+ */ const [selectedHost, setSelectedHost] = useState('');
88
+ const actingHost = selectedHost || ((_hostList_ = hostList[0]) == null ? void 0 : _hostList_.id) || '';
89
+ const mount = basePath != null ? basePath : '';
90
+ if (!orgId) {
91
+ return /*#__PURE__*/ _jsx(Alert, {
92
+ severity: "info",
93
+ children: 'Create your first site to start an organization, then browse and ' + 'install marketplace items here.'
94
+ });
95
+ }
96
+ /*
97
+ * An entity beneath the surface, not a section: `ownsSubtree` hands those
98
+ * over with no `section` set. Each owns its own chrome, and none of them
99
+ * takes the acting-site picker or the section rail — a listing is one
100
+ * thing, not a tab of the hub.
101
+ */ if (!section && (segments == null ? void 0 : segments.length)) {
102
+ if (segments[0] === 'publish' && segments[1] === 'plugin') {
103
+ return /*#__PURE__*/ _jsx(PublishPluginPage, {
104
+ orgId: orgId,
105
+ basePath: mount,
106
+ permissions: permissions,
107
+ /*
108
+ * The shell holds this whole page behind a spinner until the
109
+ * permission map settles — it fails OPEN, so rendering on it early
110
+ * offers a publish form to a member who is about to be refused.
111
+ * Mounted at all therefore means settled, and saying so here keeps
112
+ * the form's own gate honest when it is mounted anywhere else.
113
+ */ permissionsLoaded: true
114
+ });
115
+ }
116
+ if (segments[0] === 'publisher' && segments[1]) {
117
+ return /*#__PURE__*/ _jsx(PublisherProfilePage, {
118
+ segment: String(segments[1]),
119
+ basePath: mount,
120
+ actingHost: actingHost,
121
+ permissions: permissions,
122
+ orgSlug: orgSlug
123
+ });
124
+ }
125
+ // Anything else one level down is a listing id. The surface owns its
126
+ // subtree, so it also owns saying "no such thing" — which the detail page
127
+ // has to be able to do anyway for a listing deleted while a link to it
128
+ // was still in someone's inbox.
129
+ if (segments.length === 1) {
130
+ return /*#__PURE__*/ _jsx(ListingDetailPage, {
131
+ listingId: String(segments[0]),
132
+ orgId: orgId,
133
+ basePath: mount,
134
+ actingHost: actingHost,
135
+ hosts: hostList,
136
+ permissions: permissions,
137
+ orgSlug: orgSlug
138
+ });
139
+ }
140
+ return /*#__PURE__*/ _jsx(Alert, {
141
+ severity: "warning",
142
+ children: 'This marketplace page does not exist. It may have moved, or the ' + 'link may be incomplete.'
143
+ });
144
+ }
145
+ if (!actingHost) {
146
+ return /*#__PURE__*/ _jsx(Alert, {
147
+ severity: "info",
148
+ children: 'Add a site to your organization to install marketplace items — ' + 'installs apply to a site (or every site).'
149
+ });
150
+ }
151
+ return /*#__PURE__*/ _jsxs(Stack, {
152
+ spacing: 2,
153
+ children: [
154
+ /*#__PURE__*/ _jsx(MarketplacePaymentsNotice, {}),
155
+ hostList.length > 1 ? /*#__PURE__*/ _jsxs(Stack, {
156
+ direction: "row",
157
+ spacing: 1,
158
+ sx: {
159
+ alignItems: 'center',
160
+ flexWrap: 'wrap'
161
+ },
162
+ children: [
163
+ /*#__PURE__*/ _jsx(Typography, {
164
+ variant: "body2",
165
+ color: "text.secondary",
166
+ children: 'Acting site'
167
+ }),
168
+ /*#__PURE__*/ _jsx(TextField, {
169
+ select: true,
170
+ size: "small",
171
+ label: "Site",
172
+ value: actingHost,
173
+ onChange: (event)=>setSelectedHost(event.target.value),
174
+ sx: {
175
+ minWidth: 200
176
+ },
177
+ children: hostList.map((host)=>/*#__PURE__*/ _jsx(MenuItem, {
178
+ value: host.id,
179
+ children: host.label
180
+ }, host.id))
181
+ })
182
+ ]
183
+ }) : null,
184
+ /*#__PURE__*/ _jsx(HubSections, {
185
+ sections: sections != null ? sections : [],
186
+ children: section === 'browse' ? /*#__PURE__*/ _jsx(MarketplaceBrowse, {
187
+ hostId: actingHost,
188
+ permissions: permissions,
189
+ orgScoped: true,
190
+ // The shell already knows the org (AGL-867): passing it keeps
191
+ // listing links resolving synchronously instead of via an async
192
+ // hostIndex→org lookup that can come back empty and leave the
193
+ // detail page unreachable from browse.
194
+ orgSlug: orgSlug
195
+ }) : section === 'installed' ? /*#__PURE__*/ _jsxs(Stack, {
196
+ spacing: 2,
197
+ children: [
198
+ /*#__PURE__*/ _jsx(Alert, {
199
+ severity: "info",
200
+ action: /*#__PURE__*/ _jsx(AppLink, {
201
+ href: `/${orgSlug}/plugins`,
202
+ children: /*#__PURE__*/ _jsx(Button, {
203
+ size: "small",
204
+ color: "inherit",
205
+ component: "span",
206
+ children: 'Open Plugins'
207
+ })
208
+ }),
209
+ children: 'A quick list of what this organization installed from the ' + 'marketplace. Settings, per-site scope and built-in plugins ' + 'live in Plugins.'
210
+ }),
211
+ /*#__PURE__*/ _jsx(HostPluginsCard, {
212
+ hostId: actingHost,
213
+ orgSlug: orgSlug
214
+ })
215
+ ]
216
+ }) : section === 'licences' ? /*#__PURE__*/ _jsx(OrgLicencesPanel, {
217
+ orgId: orgId,
218
+ basePath: mount,
219
+ viewerOrgs: viewerOrgs
220
+ }) : section === 'upload' ? /*#__PURE__*/ _jsx(OrgPublishPanel, {
221
+ orgId: orgId,
222
+ hosts: hostList,
223
+ basePath: mount,
224
+ billingPath: orgMount == null ? void 0 : orgMount.billingPath,
225
+ org: org,
226
+ // The shell holds this page behind a spinner until the org doc
227
+ // settles, so an org in hand means an org that arrived. Stated
228
+ // rather than assumed: the fee this panel prints is a claim about
229
+ // a publisher's plan, and the free rate is what an absent doc
230
+ // resolves to.
231
+ orgReady: Boolean(org)
232
+ }) : section === 'profile' || section === 'listings' || section === 'payouts' || section === 'sales' ? /*#__PURE__*/ _jsx(OrgSellerPanel, {
233
+ orgId: orgId,
234
+ section: section,
235
+ basePath: mount
236
+ }) : null
237
+ })
238
+ ]
239
+ });
240
+ }
241
+ MarketplaceHub.displayName = 'MarketplaceHub';
242
+ export default MarketplaceHub;
243
+
244
+ //# sourceMappingURL=marketplace-hub.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/components/marketplace-hub.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 type { ConsolePluginPageProps } from '@aglyn/aglyn'\nimport { HubSections } from '@aglyn/shared-ui-next/components/hub-tabs'\nimport { AppLink } from '@aglyn/shared-ui-jsx'\nimport {\n Alert,\n Button,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useMemo, useState } from 'react'\nimport HostPluginsCard from './host-plugins-card.component'\nimport ListingDetailPage from './listing-detail-page.component'\nimport MarketplaceBrowse from './marketplace-browse.component'\nimport MarketplacePaymentsNotice from './marketplace-payments-notice.component'\nimport OrgLicencesPanel from './org-licences-panel.component'\nimport OrgPublishPanel from './org-publish-panel.component'\nimport OrgSellerPanel from './org-seller-panel.component'\nimport PublisherProfilePage from './publisher-profile-page.component'\nimport PublishPluginPage from './publish-plugin-page.component'\n\n/**\n * The Marketplace hub (AGL-772/2501), as a plugin surface (AGL-3080).\n *\n * ## What moved, and what did not\n *\n * Every section of this hub — browse, installed, licences, and the four\n * seller cards — was a hand-written console route under\n * `app/(app)/[orgSlug]/marketplace/`, ten route files whose whole job was to\n * assemble chrome around a body that already belonged to this plugin. The\n * shell's generic org plugin route does that assembly for every other\n * surface, from one declaration. So the declaration is now in `plugin.ts`\n * and this is the body.\n *\n * NO URL CHANGED. `ConsoleNavItem.ownsSubtree` is what makes that true: a\n * segment naming a declared section resolves as a section, and anything else\n * beneath the surface is handed over as `segments` for this page to read. So\n * `/{org}/marketplace/browse` is still the browse tab and\n * `/{org}/marketplace/{listingId}` is still a listing — the same addresses,\n * served by a declaration instead of by seventeen files.\n *\n * ## The gates are the shell's now, and that is the point\n *\n * The seller sections read the organization's REVENUE and its payout account,\n * and the old sections layout gated them itself — above the pages rather than\n * in the rail, because a route can be typed whether or not a tab was ever\n * offered. That is exactly what `ConsoleNavSection.permission` does, applied\n * by the same shell that hides the tab, so a hidden tab and a refused deep\n * link are one verdict instead of two copies of one. The permission map fails\n * OPEN while it loads, and the shell holds the whole page on it — so this\n * component never has to know that.\n *\n * Likewise `release_marketplace`: the sections layout wrapped itself in a\n * `FeatureGate` because the flag also feeds the plugin LOADER, and a hub that\n * went on rendering with its widgets empty made turning the feature off its\n * own broken state (AGL-2019). The shell applies the same flag through the\n * nav item's `navTabId`, around the same page.\n */\nexport function MarketplaceHub(props: ConsolePluginPageProps) {\n const {\n orgMount,\n basePath,\n sections,\n section,\n segments,\n permissions,\n viewerOrgs,\n org,\n } = props\n\n const orgId = orgMount?.orgId ?? null\n const orgSlug = orgMount?.orgSlug ?? ''\n const hostList = useMemo(\n () => (orgMount?.hosts ?? []).map((host) => ({ id: host.id, label: host.name })),\n [orgMount?.hosts],\n )\n /**\n * The site an install acts through. Installs pin to a site and pins are\n * validated against host membership, so every section that installs needs\n * the same answer — which is why it is chosen once here rather than per\n * section.\n */\n const [selectedHost, setSelectedHost] = useState('')\n const actingHost = selectedHost || hostList[0]?.id || ''\n\n const mount = basePath ?? ''\n\n if (!orgId) {\n return (\n <Alert severity=\"info\">\n {'Create your first site to start an organization, then browse and ' +\n 'install marketplace items here.'}\n </Alert>\n )\n }\n\n /*\n * An entity beneath the surface, not a section: `ownsSubtree` hands those\n * over with no `section` set. Each owns its own chrome, and none of them\n * takes the acting-site picker or the section rail — a listing is one\n * thing, not a tab of the hub.\n */\n if (!section && segments?.length) {\n if (segments[0] === 'publish' && segments[1] === 'plugin') {\n return (\n <PublishPluginPage\n orgId={orgId}\n basePath={mount}\n permissions={permissions}\n /*\n * The shell holds this whole page behind a spinner until the\n * permission map settles — it fails OPEN, so rendering on it early\n * offers a publish form to a member who is about to be refused.\n * Mounted at all therefore means settled, and saying so here keeps\n * the form's own gate honest when it is mounted anywhere else.\n */\n permissionsLoaded\n />\n )\n }\n if (segments[0] === 'publisher' && segments[1]) {\n return (\n <PublisherProfilePage\n segment={String(segments[1])}\n basePath={mount}\n actingHost={actingHost}\n permissions={permissions}\n orgSlug={orgSlug}\n />\n )\n }\n // Anything else one level down is a listing id. The surface owns its\n // subtree, so it also owns saying \"no such thing\" — which the detail page\n // has to be able to do anyway for a listing deleted while a link to it\n // was still in someone's inbox.\n if (segments.length === 1) {\n return (\n <ListingDetailPage\n listingId={String(segments[0])}\n orgId={orgId}\n basePath={mount}\n actingHost={actingHost}\n hosts={hostList}\n permissions={permissions}\n orgSlug={orgSlug}\n />\n )\n }\n return (\n <Alert severity=\"warning\">\n {'This marketplace page does not exist. It may have moved, or the ' +\n 'link may be incomplete.'}\n </Alert>\n )\n }\n\n if (!actingHost) {\n return (\n <Alert severity=\"info\">\n {'Add a site to your organization to install marketplace items — ' +\n 'installs apply to a site (or every site).'}\n </Alert>\n )\n }\n\n return (\n <Stack spacing={2}>\n {/*\n What this deployment cannot do, said BEFORE the click (AGL-2019). The\n app supplies only the fact, through the deployment-capability context\n its org layout provides; the sentence is this plugin's, because it\n names what still works without a Stripe platform. Draws nothing at all\n on a configured deployment.\n */}\n <MarketplacePaymentsNotice />\n {hostList.length > 1 ? (\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', flexWrap: 'wrap' }}\n >\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Acting site'}\n </Typography>\n <TextField\n select\n size=\"small\"\n label=\"Site\"\n value={actingHost}\n onChange={(event) => setSelectedHost(event.target.value)}\n sx={{ minWidth: 200 }}\n >\n {hostList.map((host) => (\n <MenuItem key={host.id} value={host.id}>\n {host.label}\n </MenuItem>\n ))}\n </TextField>\n </Stack>\n ) : null}\n <HubSections sections={sections ?? []}>\n {section === 'browse' ? (\n <MarketplaceBrowse\n hostId={actingHost}\n permissions={permissions}\n orgScoped\n // The shell already knows the org (AGL-867): passing it keeps\n // listing links resolving synchronously instead of via an async\n // hostIndex→org lookup that can come back empty and leave the\n // detail page unreachable from browse.\n orgSlug={orgSlug}\n />\n ) : section === 'installed' ? (\n <Stack spacing={2}>\n {/* A convenience, not the inventory (AGL-1011). Administering what\n you already run belongs in Plugins; this stays so uninstalling\n something you just installed does not send you elsewhere. */}\n <Alert\n severity=\"info\"\n action={\n <AppLink href={`/${orgSlug}/plugins`}>\n <Button size=\"small\" color=\"inherit\" component=\"span\">\n {'Open Plugins'}\n </Button>\n </AppLink>\n }\n >\n {'A quick list of what this organization installed from the ' +\n 'marketplace. Settings, per-site scope and built-in plugins ' +\n 'live in Plugins.'}\n </Alert>\n <HostPluginsCard hostId={actingHost} orgSlug={orgSlug} />\n </Stack>\n ) : section === 'licences' ? (\n <OrgLicencesPanel\n orgId={orgId}\n basePath={mount}\n viewerOrgs={viewerOrgs}\n />\n ) : section === 'upload' ? (\n <OrgPublishPanel\n orgId={orgId}\n hosts={hostList}\n basePath={mount}\n billingPath={orgMount?.billingPath}\n org={org as never}\n // The shell holds this page behind a spinner until the org doc\n // settles, so an org in hand means an org that arrived. Stated\n // rather than assumed: the fee this panel prints is a claim about\n // a publisher's plan, and the free rate is what an absent doc\n // resolves to.\n orgReady={Boolean(org)}\n />\n ) : section === 'profile' ||\n section === 'listings' ||\n section === 'payouts' ||\n section === 'sales' ? (\n <OrgSellerPanel orgId={orgId} section={section} basePath={mount} />\n ) : null}\n </HubSections>\n </Stack>\n )\n}\n\nMarketplaceHub.displayName = 'MarketplaceHub'\n\nexport default MarketplaceHub\n"],"names":["HubSections","AppLink","Alert","Button","MenuItem","Stack","TextField","Typography","useMemo","useState","HostPluginsCard","ListingDetailPage","MarketplaceBrowse","MarketplacePaymentsNotice","OrgLicencesPanel","OrgPublishPanel","OrgSellerPanel","PublisherProfilePage","PublishPluginPage","MarketplaceHub","props","hostList","orgMount","basePath","sections","section","segments","permissions","viewerOrgs","org","orgId","orgSlug","hosts","map","host","id","label","name","selectedHost","setSelectedHost","actingHost","mount","severity","length","permissionsLoaded","segment","String","listingId","spacing","direction","sx","alignItems","flexWrap","variant","color","select","size","value","onChange","event","target","minWidth","hostId","orgScoped","action","href","component","billingPath","orgReady","Boolean","displayName"],"mappings":"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"}