@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.
- package/package.json +15 -11
- package/src/lib/components/edit-listing-dialog.component.d.ts +20 -0
- package/src/lib/components/edit-listing-dialog.component.js +51 -0
- package/src/lib/components/edit-listing-dialog.component.js.map +1 -0
- package/src/lib/components/listing-detail-editor.component.d.ts +24 -0
- package/src/lib/components/listing-detail-editor.component.js +457 -0
- package/src/lib/components/listing-detail-editor.component.js.map +1 -0
- package/src/lib/components/listing-detail-page.component.d.ts +28 -0
- package/src/lib/components/listing-detail-page.component.js +151 -0
- package/src/lib/components/listing-detail-page.component.js.map +1 -0
- package/src/lib/components/listing-review-status.component.d.ts +67 -0
- package/src/lib/components/listing-review-status.component.js +301 -0
- package/src/lib/components/listing-review-status.component.js.map +1 -0
- package/src/lib/components/listing-verification-request.component.d.ts +40 -0
- package/src/lib/components/listing-verification-request.component.js +170 -0
- package/src/lib/components/listing-verification-request.component.js.map +1 -0
- package/src/lib/components/marketplace-hub.component.d.ts +43 -0
- package/src/lib/components/marketplace-hub.component.js +244 -0
- package/src/lib/components/marketplace-hub.component.js.map +1 -0
- package/src/lib/components/marketplace-reports-page.component.d.ts +23 -0
- package/src/lib/components/marketplace-reports-page.component.js +324 -0
- package/src/lib/components/marketplace-reports-page.component.js.map +1 -0
- package/src/lib/components/org-licences-panel.component.d.ts +73 -0
- package/src/lib/components/org-licences-panel.component.js +318 -0
- package/src/lib/components/org-licences-panel.component.js.map +1 -0
- package/src/lib/components/org-publish-panel.component.d.ts +45 -0
- package/src/lib/components/org-publish-panel.component.js +402 -0
- package/src/lib/components/org-publish-panel.component.js.map +1 -0
- package/src/lib/components/org-seller-panel.component.d.ts +28 -0
- package/src/lib/components/org-seller-panel.component.js +956 -0
- package/src/lib/components/org-seller-panel.component.js.map +1 -0
- package/src/lib/components/plugin-review-detail.component.d.ts +16 -0
- package/src/lib/components/plugin-review-detail.component.js +1438 -0
- package/src/lib/components/plugin-review-detail.component.js.map +1 -0
- package/src/lib/components/plugin-reviews-queue.component.d.ts +13 -0
- package/src/lib/components/plugin-reviews-queue.component.js +385 -0
- package/src/lib/components/plugin-reviews-queue.component.js.map +1 -0
- package/src/lib/components/plugin-reviews-surface.component.d.ts +19 -0
- package/src/lib/components/plugin-reviews-surface.component.js +46 -0
- package/src/lib/components/plugin-reviews-surface.component.js.map +1 -0
- package/src/lib/components/publish-plugin-form.component.d.ts +61 -0
- package/src/lib/components/publish-plugin-form.component.js +857 -0
- package/src/lib/components/publish-plugin-form.component.js.map +1 -0
- package/src/lib/components/publish-plugin-page.component.d.ts +29 -0
- package/src/lib/components/publish-plugin-page.component.js +92 -0
- package/src/lib/components/publish-plugin-page.component.js.map +1 -0
- package/src/lib/components/publisher-profile-page.component.d.ts +13 -0
- package/src/lib/components/publisher-profile-page.component.js +261 -0
- package/src/lib/components/publisher-profile-page.component.js.map +1 -0
- package/src/lib/constants/listing-media.d.ts +28 -0
- package/src/lib/constants/listing-media.js +28 -0
- package/src/lib/constants/listing-media.js.map +1 -0
- package/src/lib/model/listing-social-card.d.ts +94 -0
- package/src/lib/model/listing-social-card.js +177 -0
- package/src/lib/model/listing-social-card.js.map +1 -0
- package/src/lib/model/marketplace-paths.d.ts +54 -0
- package/src/lib/model/marketplace-paths.js +59 -0
- package/src/lib/model/marketplace-paths.js.map +1 -0
- package/src/lib/model/payout-readiness.d.ts +81 -0
- package/src/lib/model/payout-readiness.js +52 -0
- package/src/lib/model/payout-readiness.js.map +1 -0
- package/src/lib/model/plugin-rejection-categories.d.ts +66 -0
- package/src/lib/model/plugin-rejection-categories.js +124 -0
- package/src/lib/model/plugin-rejection-categories.js.map +1 -0
- package/src/lib/model/plugin-review-checklist.d.ts +62 -0
- package/src/lib/model/plugin-review-checklist.js +131 -0
- package/src/lib/model/plugin-review-checklist.js.map +1 -0
- package/src/lib/model/plugin-review-status.d.ts +104 -0
- package/src/lib/model/plugin-review-status.js +126 -0
- package/src/lib/model/plugin-review-status.js.map +1 -0
- package/src/lib/model/seller-ledger-totals.d.ts +100 -0
- package/src/lib/model/seller-ledger-totals.js +96 -0
- package/src/lib/model/seller-ledger-totals.js.map +1 -0
- package/src/lib/plugin.js +170 -40
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/server/admin-reports.d.ts +18 -0
- package/src/lib/server/admin-reports.js +280 -0
- package/src/lib/server/admin-reports.js.map +1 -0
- package/src/lib/server/admin-reviews.d.ts +18 -0
- package/src/lib/server/admin-reviews.js +1341 -0
- package/src/lib/server/admin-reviews.js.map +1 -0
- package/src/lib/server/listing-social-card.server.d.ts +24 -0
- package/src/lib/server/listing-social-card.server.js +65 -0
- package/src/lib/server/listing-social-card.server.js.map +1 -0
- package/src/lib/server/publisher-review-email.d.ts +54 -0
- package/src/lib/server/publisher-review-email.js +85 -0
- package/src/lib/server/publisher-review-email.js.map +1 -0
- package/src/lib/server/revoke-cache-drop.d.ts +59 -0
- package/src/lib/server/revoke-cache-drop.js +74 -0
- package/src/lib/server/revoke-cache-drop.js.map +1 -0
- package/src/lib/server.js +47 -0
- package/src/lib/server.js.map +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/components/publish-plugin-form.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n formatMediaRef,\n isBelowMarketplacePriceFloor,\n marketplacePriceCostNote,\n marketplacePriceFloorHint,\n offeredPluginVersion,\n useMediaPicker,\n type PluginRevocation,\n} from '@aglyn/aglyn'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Button,\n Checkbox,\n Chip,\n FormControlLabel,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n missingAttestationSubjects,\n PUBLISHER_ATTESTATION,\n requiredAttestationIds,\n} from '@aglyn/aglyn/app-utils/publisher-attestation'\nimport {\n MarkdownField,\n type MarkdownFieldHandle,\n} from '@aglyn/aglyn-markdown-editor'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { inheritedMediaAlt } from '@aglyn/aglyn/app-utils/media-metadata'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { listingPath } from '../model/marketplace-paths'\n\n// Mirrors the server taxonomy; the server re-validates (the console cannot\n// import the marketplace plugin's model — scope:app may not reach aglyn:addons).\nconst LISTING_CATEGORIES = [\n 'analytics',\n 'automation',\n 'commerce',\n 'communication',\n 'content',\n 'design',\n 'forms',\n 'integrations',\n 'marketing',\n 'productivity',\n 'seo',\n 'security',\n] as const\n\n// Mirrors `validateListingContent`'s URL rule, which is what the publish\n// route enforces — this only saves a round trip (AGL-1076).\nconst isHttpsUrl = (value: string) =>\n /^https:\\/\\/[^\\s]+$/.test(value.trim()) && value.trim().length <= 500\n\n/** Base64-encode file bytes without a data: prefix. */\nasync function fileToBase64(file: File): Promise<string> {\n const buffer = await file.arrayBuffer()\n let binary = ''\n const bytes = new Uint8Array(buffer)\n for (let i = 0; i < bytes.length; i += 1)\n binary += String.fromCharCode(bytes[i])\n return btoa(binary)\n}\n\n/**\n * Everything the draft can hold.\n *\n * The bundle File is deliberately absent. A File cannot be serialized, and\n * a draft that silently restores every field except the one that decides\n * what actually ships is worse than no draft — the publisher would believe\n * they were republishing the bytes they left with. It is re-chosen, and the\n * page says so rather than pretending otherwise.\n */\ninterface PublishDraft {\n manifestText: string\n displayName: string\n description: string\n category: string\n changelog: string\n priceUsd: string\n readme: string\n license: string\n repositoryUrl: string\n visibility: 'public' | 'private'\n attested: string[]\n}\n\n/**\n * One verifier finding as the publish API returns it (AGL-1091). Declared\n * locally rather than imported: the shared type lives in the server barrel,\n * which carries the parser this form must never ship to a browser.\n */\ninterface PublishProblem {\n level?: string\n message: string\n /** Which check produced it (AGL-1087) — absent on older responses. */\n check?: string\n}\n\nconst EMPTY_DRAFT: PublishDraft = {\n manifestText: '',\n displayName: '',\n description: '',\n category: '',\n changelog: '',\n priceUsd: '0',\n readme: '',\n license: '',\n repositoryUrl: '',\n visibility: 'public',\n attested: [],\n}\n\n// Scoped per listing (AGL-1008): an update draft and a new-listing draft\n// are different submissions, and sharing one key would let a half-written\n// update reappear on top of a first publish.\nconst draftKey = (orgId: string, listingId?: string) =>\n `aglyn.publish-plugin.draft.${orgId}${listingId ? `.${listingId}` : ''}`\n\n/**\n * Read the stored draft, during render.\n *\n * Deliberately NOT an effect. Restoring in one effect while a second effect\n * persists on every change is a race that Strict Mode wins: React\n * double-invokes effects in development, so the persist pass writes the\n * still-empty state back over storage BEFORE the restore's `setDraft` has\n * applied, and the re-run then restores the emptiness it just wrote. The\n * draft looked correct in jsdom (no Strict Mode) and silently vanished in\n * the browser. A read during initialization has no ordering to lose.\n */\nfunction readDraft(orgId: string, listingId?: string): PublishDraft {\n if (typeof window === 'undefined' || !orgId) return EMPTY_DRAFT\n try {\n const stored = window.localStorage.getItem(draftKey(orgId, listingId))\n if (!stored) return EMPTY_DRAFT\n return { ...EMPTY_DRAFT, ...(JSON.parse(stored) as Partial<PublishDraft>) }\n } catch {\n // A corrupt draft is not worth failing the page over.\n return EMPTY_DRAFT\n }\n}\n\n/**\n * Fill an update's form from the listing it is bound to (AGL-1008).\n *\n * A saved draft wins over the listing on every field: the listing is where\n * you START from, and overwriting an edit someone made and reloaded away\n * from would be the same data loss the draft exists to prevent.\n *\n * `changelog` is deliberately never seeded. It is the one field that\n * describes THIS version, and carrying the previous version's text forward\n * is how a changelog ends up describing a release it has nothing to do\n * with — which the AGL-969 attestation then asks the publisher to confirm.\n */\nfunction seedDraft(\n draft: PublishDraft,\n listing: UpdateTargetListing | null | undefined,\n): PublishDraft {\n if (!listing?.$id) return draft\n const seeded = { ...draft }\n const carry = <K extends keyof PublishDraft>(\n key: K,\n value: string | undefined,\n ) => {\n if (seeded[key] === EMPTY_DRAFT[key] && value) {\n seeded[key] = value as PublishDraft[K]\n }\n }\n carry('displayName', listing.displayName)\n carry('description', listing.description)\n carry('category', listing.categories?.[0] ?? listing.category)\n carry('readme', listing.readme)\n carry('license', listing.license)\n carry('repositoryUrl', listing.repositoryUrl)\n if (seeded.priceUsd === EMPTY_DRAFT.priceUsd && listing.priceUsd) {\n seeded.priceUsd = String(listing.priceUsd)\n }\n // Visibility is set on FIRST publish only — the server ignores it on a\n // version bump — so reflect what the listing actually is rather than\n // offering a choice that would be silently discarded.\n if (listing.visibility === 'private') seeded.visibility = 'private'\n return seeded\n}\n\n/**\n * Whether a stored draft is worth telling anyone about.\n *\n * The persist effect runs on mount, so an untouched form writes its\n * starting state straight back — and without this the NEXT first visit\n * would announce \"picked up where you left off\" over a form nobody had\n * typed in. A restore notice that fires when nothing was restored is how a\n * publisher learns to ignore it, which is expensive on the one visit it\n * matters.\n *\n * Compared against the BASELINE rather than the empty draft: on an update\n * the baseline is the listing's own content (AGL-1008), so an untouched\n * update form is not a resumed one.\n */\nfunction draftDiffersFrom(\n draft: Partial<PublishDraft>,\n baseline: PublishDraft,\n): boolean {\n return (Object.keys(EMPTY_DRAFT) as Array<keyof PublishDraft>).some((key) => {\n const value = draft[key]\n if (value === undefined) return false\n if (Array.isArray(value)) {\n const other = baseline[key]\n return (\n !Array.isArray(other) ||\n value.length !== other.length ||\n value.some((entry, index) => entry !== other[index])\n )\n }\n return value !== baseline[key]\n })\n}\n\n/** The listing an update is bound to (AGL-1008), as the console reads it. */\nexport interface UpdateTargetListing {\n $id: string\n displayName?: string\n pluginId?: string\n latestVersion?: string | number\n latestApprovedVersion?: string | number\n description?: string\n category?: string\n categories?: string[]\n readme?: string\n license?: string\n repositoryUrl?: string\n priceUsd?: number\n visibility?: string\n}\n\nexport interface PublishPluginFormProps {\n orgId: string\n /**\n * Where the marketplace hub is mounted under this organization, from the\n * shell (AGL-3080). The listing the publish lands on hangs beneath it; it\n * replaced an org slug and the console's route table, which a plugin may\n * not import.\n */\n basePath: string\n /**\n * Set when publishing a NEW VERSION of an existing listing (AGL-1008).\n * The server still decides whether a submission is an update, from\n * `profileId` + the manifest id — this only pre-binds the form so the\n * publisher is not retyping a listing they already own, and so the page\n * can say what is about to happen.\n */\n listing?: UpdateTargetListing | null\n /**\n * The listing's kill switch (AGL-2368), so `liveVersion` below can answer\n * \"what installs today\" rather than \"what review last approved\". Those are\n * different questions and `latestApprovedVersion` only answers the second.\n */\n revocation?: PluginRevocation | null\n}\n\n/**\n * The plugin publish form (AGL-1078), as a page.\n *\n * It was a modal. A publish is the most consequential thing a publisher\n * does — it puts executable code in front of every workspace that installs\n * it — and it had no URL to link or resume, no draft, and no room, which is\n * why the AGL-969 checklist ended up below the publish button where it had\n * to be scrolled past to be read.\n *\n * Same server contract: this posts the identical body to\n * `/api/marketplace/publish-plugin`. What changed is the container, and what\n * the container makes possible — sections, a draft that survives a reload,\n * server problems shown against the field that caused them, and somewhere\n * to go afterwards.\n */\nexport function PublishPluginForm(props: PublishPluginFormProps) {\n const { orgId, basePath, listing, revocation } = props\n const isUpdate = Boolean(listing?.$id)\n const { data: user } = useUser()\n const router = useRouter()\n const { enqueueSnackbar } = useSnackbar()\n const [busy, setBusy] = useState(false)\n\n const bundleRef = useRef<HTMLInputElement>(null)\n const manifestRef = useRef<HTMLInputElement>(null)\n const [bundleFile, setBundleFile] = useState<File | null>(null)\n const [manifestFile, setManifestFile] = useState<File | null>(null)\n // Inline README images come from the shared DAM, like everywhere else.\n const readmeEditorRef = useRef<MarkdownFieldHandle | null>(null)\n /*\n * The console's media library, opened through the picker context rather\n * than by mounting its dialog (AGL-3080): the library is coupled to the\n * org and the session, which a plugin has no way to reach, and the console\n * provides `pickMedia` around every plugin page for exactly this.\n */\n const { pickMedia } = useMediaPicker()\n const insertReadmeImage = useCallback(async () => {\n const media = await pickMedia?.({ kind: 'image' })\n if (!media) return\n // A reference, not the origin-absolute CDN URL (AGL-1705) — the same\n // switch the listing detail editor's README body makes, and for the\n // reason established there: this markdown renders only in the console,\n // through the one `marketplaceListing` slot, so nothing was relying on\n // the baked-in origin. The URL stays the fallback for a free-tier asset,\n // which is minted no `cdnPath` and so has no reference to name.\n const src = formatMediaRef(media.mediaScope, media.mediaId) ?? media.url\n // The alt was a hardcoded '' (AGL-1896). Same reasoning as the listing\n // detail editor: no alt prompt exists on this surface, and a\n // markdown-lite image row's alt is fixed at insert time — so the asset's\n // own alt is the only description that could ever land here, and '' was\n // otherwise permanent.\n if (src)\n readmeEditorRef.current?.insertImage(\n inheritedMediaAlt({ assetAlt: media.alt }) ?? '',\n src,\n )\n }, [pickMedia])\n // What an untouched form looks like: empty for a first publish, the\n // listing's own content for an update (AGL-1008).\n const baseline = seedDraft(EMPTY_DRAFT, listing)\n const [draft, setDraft] = useState<PublishDraft>(() =>\n seedDraft(readDraft(orgId, listing?.$id), listing),\n )\n const [draftRestored, setDraftRestored] = useState(() =>\n draftDiffersFrom(\n seedDraft(readDraft(orgId, listing?.$id), listing),\n seedDraft(EMPTY_DRAFT, listing),\n ),\n )\n\n // Server-side problems, held against the field that caused them (AGL-1078)\n // instead of thrown at a snackbar that is gone before it is read. Cleared\n // on every submit so a fixed problem stops being shown as one.\n //\n // Objects, not strings (AGL-1091): the verifier has always answered with\n // `{level, message, check}` and this held `string[]`, so rendering a\n // rejection threw \"objects are not valid as a React child\" and the error\n // boundary ate the very card that was supposed to explain the rejection.\n // Normalized on arrival so a plain string from anywhere else still reads.\n const [problems, setProblems] = useState<PublishProblem[]>([])\n const [missingAttestations, setMissingAttestations] = useState<string[]>([])\n const [agreementProblem, setAgreementProblem] = useState('')\n const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({})\n\n const set = <K extends keyof PublishDraft>(key: K) =>\n (value: PublishDraft[K]) =>\n setDraft((current) => ({ ...current, [key]: value }))\n\n // Written back on every change — one small JSON blob, and the only thing\n // that makes a reload safe. Safe to run on the first pass now that the\n // restore happens during initialization: the value it writes IS the\n // restored one, so there is nothing left to clobber.\n useEffect(() => {\n if (!orgId) return\n try {\n window.localStorage.setItem(\n draftKey(orgId, listing?.$id),\n JSON.stringify(draft),\n )\n } catch {\n // Storage full or blocked — the form still works, it just won't survive.\n }\n }, [orgId, listing?.$id, draft])\n\n const clearDraft = useCallback(() => {\n try {\n window.localStorage.removeItem(draftKey(orgId, listing?.$id))\n } catch {\n // Nothing to do; the next publish overwrites it anyway.\n }\n }, [orgId, listing?.$id])\n\n const toggleAttestation = (id: string) =>\n setDraft((current) => ({\n ...current,\n attested: current.attested.includes(id)\n ? current.attested.filter((entry) => entry !== id)\n : [...current.attested, id],\n }))\n\n /**\n * What this page blocks on locally.\n *\n * On a first publish the changelog item is `updateOnly` and this form\n * genuinely cannot know whether the manifest id already has a listing —\n * the server answers that and names it in a 428. Demanding a changelog\n * attestation from a first-time publisher would be the worse failure: an\n * item that does not apply teaches people to tick without reading.\n *\n * Bound to a listing (AGL-1008) we DO know, so the update-only item is\n * asked here rather than arriving as a refusal after the upload.\n */\n const unattested = requiredAttestationIds(isUpdate).filter(\n (id) => !draft.attested.includes(id),\n )\n const unfilledSubjects = missingAttestationSubjects(\n { repositoryUrl: draft.repositoryUrl },\n isUpdate,\n )\n const repositoryInvalid =\n Boolean(draft.repositoryUrl.trim()) && !isHttpsUrl(draft.repositoryUrl)\n\n /**\n * The manifest id typed or pasted so far, if it parses.\n *\n * Only used to catch the one way a pre-bound update silently isn't one:\n * the server decides update-vs-new from `profileId` + the manifest id, so\n * a bundle whose id does not match this listing creates a SECOND listing\n * while the page says \"new version\". Cheap to notice here; impossible to\n * undo afterwards, because a published version is immutable.\n */\n const typedManifestId = (() => {\n const raw = draft.manifestText.trim()\n if (!raw) return ''\n try {\n const parsed = JSON.parse(raw) as { id?: unknown }\n return typeof parsed?.id === 'string' ? parsed.id : ''\n } catch {\n return ''\n }\n })()\n const manifestIdMismatch = Boolean(\n isUpdate && typedManifestId && listing?.pluginId &&\n typedManifestId !== listing.pluginId,\n )\n\n /**\n * What is serving customers right now (AGL-2368).\n *\n * This prints as \"v{X} is what installs today\", which is an INSTALL claim,\n * and it was reading a REVIEW verdict — the kill switch does not clear\n * `latestApprovedVersion`, so a publisher whose live version had been\n * stopped was told it was still installing, on the page they had come to\n * to ship its replacement.\n *\n * Falls through to nothing rather than to `latestVersion` when the offer is\n * revoked: the alert's other branch already says \"nothing installs until a\n * reviewer approves it\", which is the truth in that state.\n */\n const liveVersion = String(\n offeredPluginVersion(listing, revocation) ?? listing?.latestVersion ?? '',\n )\n\n const readManifest = async (): Promise<Record<string, unknown> | null> => {\n // A pasted manifest wins; otherwise read the chosen file.\n const raw = draft.manifestText.trim()\n ? draft.manifestText\n : manifestFile\n ? await manifestFile.text()\n : ''\n if (!raw) return null\n try {\n const parsed = JSON.parse(raw)\n return parsed && typeof parsed === 'object' ? parsed : null\n } catch {\n return null\n }\n }\n\n const submit = async () => {\n setProblems([])\n setMissingAttestations([])\n setAgreementProblem('')\n setFieldErrors({})\n if (!bundleFile) {\n setFieldErrors({ bundle: 'Choose a plugin bundle (.js) to upload.' })\n return\n }\n const manifest = await readManifest()\n if (!manifest) {\n setFieldErrors({\n manifest: 'Add a valid manifest (JSON) — id, name, version and entry.',\n })\n return\n }\n setBusy(true)\n try {\n const bundle = await fileToBase64(bundleFile)\n const response = await authorizedFetch(\n user,\n '/api/marketplace/publish-plugin',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n orgId,\n bundle,\n manifest,\n displayName:\n draft.displayName.trim() ||\n String((manifest as { name?: unknown }).name ?? '').trim(),\n description: draft.description.trim(),\n category: draft.category || undefined,\n changelog: draft.changelog.trim(),\n // A private plugin has no buyers — its only audience already owns\n // it — so it publishes free regardless of what the field held\n // before the publisher switched visibility.\n priceUsd:\n draft.visibility === 'private'\n ? 0\n : Math.max(0, Math.round(Number(draft.priceUsd) || 0)),\n visibility: draft.visibility,\n attestation: draft.attested,\n ...(draft.readme.trim() ? { readme: draft.readme.trim() } : {}),\n ...(draft.license.trim() ? { license: draft.license.trim() } : {}),\n ...(draft.repositoryUrl.trim()\n ? { repositoryUrl: draft.repositoryUrl.trim() }\n : {}),\n }),\n },\n )\n const payload = await response.json().catch(() => ({}))\n if (!response.ok) {\n if (Array.isArray(payload?.problems)) {\n setProblems(\n payload.problems.map((problem: unknown) =>\n typeof problem === 'string'\n ? { message: problem }\n : {\n level: String(\n (problem as PublishProblem)?.level ?? 'error',\n ),\n message: String((problem as PublishProblem)?.message ?? ''),\n check: (problem as PublishProblem)?.check\n ? String((problem as PublishProblem).check)\n : undefined,\n },\n ),\n )\n }\n if (Array.isArray(payload?.missingAttestations)) {\n setMissingAttestations(payload.missingAttestations)\n }\n // A 412 with an `agreement` is a precondition on the ORG (AGL-1077),\n // fixed on another page — held as an alert, not a toast.\n if (payload?.agreement) {\n setAgreementProblem(String(payload.error ?? ''))\n }\n // A rejected subject names its field (AGL-1076), so put the message\n // where the value is rather than in a banner about a form.\n if (Array.isArray(payload?.missingAttestationSubjects)) {\n setFieldErrors(\n Object.fromEntries(\n payload.missingAttestationSubjects.map((field: string) => [\n field,\n String(payload.error ?? 'Required'),\n ]),\n ),\n )\n }\n return void enqueueSnackbar(payload?.error ?? 'Publish failed', {\n variant: response.status === 501 ? 'info' : 'error',\n allowDuplicate: true,\n })\n }\n // Somewhere to go (AGL-1078). The dialog closed onto the panel it\n // opened from, which said nothing about what had just happened; the\n // listing shows the version now queued for review.\n clearDraft()\n enqueueSnackbar(\n `Published v${payload?.version ?? '1'} — it is queued for review`,\n { variant: 'success' },\n )\n router.push(listingPath(basePath, String(payload?.listingId ?? '')))\n } catch (error) {\n console.error(error)\n enqueueSnackbar('An error has occurred', {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n setBusy(false)\n }\n }\n\n // A price under the floor is refused by the publish route (AGL-2343), so the\n // button holds rather than letting a publisher upload a bundle and read the\n // minimum off a 400. A private plugin has no price at all — its field is\n // disabled and sends 0 — so it can never be blocked by this.\n const priceBelowFloor =\n draft.visibility !== 'private' &&\n isBelowMarketplacePriceFloor(draft.priceUsd)\n\n const blocked =\n busy ||\n !bundleFile ||\n unattested.length > 0 ||\n unfilledSubjects.length > 0 ||\n repositoryInvalid ||\n manifestIdMismatch ||\n priceBelowFloor\n\n return (\n <Stack spacing={2}>\n {isUpdate ? (\n /* AGL-1008. The mechanism was always right — same profileId plus\n manifest id bumps the listing, the new version enters review as\n pending, and the approved one keeps installing (AGL-966). The UI\n never told that story, so updating read like creating a second\n listing. */\n <Alert severity=\"info\">\n <Typography variant=\"subtitle2\">\n {`New version of ${listing?.displayName ?? 'your plugin'}`}\n </Typography>\n <Typography variant=\"caption\" component=\"div\">\n {liveVersion\n ? `v${liveVersion} is what installs today. It keeps installing ` +\n 'until a reviewer approves this one — nobody is upgraded ' +\n 'onto unreviewed code, and nothing you publish here can ' +\n 'change a version already installed.'\n : 'This version enters review. Nothing installs until a ' +\n 'reviewer approves it.'}\n </Typography>\n </Alert>\n ) : (\n <Alert severity=\"info\">\n {'Plugins publish sandboxed — they run isolated and can’t touch ' +\n 'site data directly. A reviewer verifies and signs yours before ' +\n 'it can run trusted.'}\n </Alert>\n )}\n\n {manifestIdMismatch ? (\n <Alert severity=\"warning\">\n {`This manifest says \"${typedManifestId}\", but ${\n listing?.displayName ?? 'this listing'\n } publishes \"${listing?.pluginId}\". Publishing it would create a ` +\n 'separate new listing rather than a new version of this one.'}\n </Alert>\n ) : null}\n\n {draftRestored ? (\n <Alert severity=\"info\" onClose={() => setDraftRestored(false)}>\n {'Picked up where you left off. Your bundle and manifest file are ' +\n 'not saved — choose them again before publishing.'}\n </Alert>\n ) : null}\n\n {agreementProblem ? (\n <Alert severity=\"warning\">{agreementProblem}</Alert>\n ) : null}\n\n <CardDisplay\n header={isUpdate ? 'The new bundle and manifest' : 'Bundle and manifest'}\n help={pluginDocsHelp('publisherHandbook', {\n anchor: '#publishing-a-version',\n excerpt:\n 'One self-contained bundle plus its manifest — verified before ' +\n 'it is stored, and content-addressed once it is.',\n })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <Stack spacing={0.5}>\n <Typography variant=\"subtitle2\">{'Bundle (.js)'}</Typography>\n <input\n ref={bundleRef}\n type=\"file\"\n accept=\".js,text/javascript,application/javascript\"\n hidden\n onChange={(event) =>\n setBundleFile(event.target.files?.[0] ?? null)\n }\n />\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Button size=\"small\" onClick={() => bundleRef.current?.click()}>\n {'Choose bundle'}\n </Button>\n <Typography variant=\"body2\" color=\"text.secondary\" noWrap>\n {bundleFile?.name ?? 'No file chosen'}\n </Typography>\n </Stack>\n <Typography\n variant=\"caption\"\n color={fieldErrors['bundle'] ? 'error.main' : 'text.secondary'}\n >\n {fieldErrors['bundle'] ??\n 'One self-contained bundle. It’s parsed and checked before ' +\n 'publishing — forbidden APIs, size, and every network ' +\n 'call against the origins your manifest declares.'}\n </Typography>\n </Stack>\n\n <Stack spacing={0.5}>\n <Typography variant=\"subtitle2\">\n {'Manifest (JSON: id, name, version, entry)'}\n </Typography>\n <input\n ref={manifestRef}\n type=\"file\"\n accept=\".json,application/json\"\n hidden\n onChange={(event) =>\n setManifestFile(event.target.files?.[0] ?? null)\n }\n />\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Button size=\"small\" onClick={() => manifestRef.current?.click()}>\n {'Choose manifest.json'}\n </Button>\n <Typography variant=\"body2\" color=\"text.secondary\" noWrap>\n {manifestFile?.name ?? 'or paste below'}\n </Typography>\n </Stack>\n <TextField\n placeholder='{ \"id\": \"acme-widget\", \"name\": \"Widget\", \"version\": \"1.0.0\", \"entry\": \"index.js\" }'\n value={draft.manifestText}\n onChange={(event) => set('manifestText')(event.target.value)}\n size=\"small\"\n multiline\n minRows={4}\n fullWidth\n error={Boolean(fieldErrors['manifest'])}\n helperText={fieldErrors['manifest'] ?? ' '}\n />\n </Stack>\n\n {problems.length ? (\n <Alert\n severity={\n problems.some((problem) => problem.level !== 'warning')\n ? 'error'\n : 'warning'\n }\n >\n <Typography variant=\"subtitle2\">\n {problems.some((problem) => problem.level !== 'warning')\n ? 'Bundle failed verification'\n : 'Bundle verified, with things to check'}\n </Typography>\n <ul style={{ margin: '4px 0 0', paddingLeft: 18 }}>\n {problems.map((problem, index) => (\n <li key={`${problem.check ?? 'problem'}-${index}`}>\n <Typography variant=\"caption\">\n {problem.check ? `${problem.check}: ` : ''}\n {problem.message}\n </Typography>\n </li>\n ))}\n </ul>\n </Alert>\n ) : null}\n </Stack>\n </CardDisplay>\n\n <CardDisplay\n header={'Listing'}\n help={pluginDocsHelp('publisherHandbook', {\n anchor: '#authoring-your-listing',\n excerpt:\n 'What a reviewer reads first and a buyer reads before running ' +\n 'third-party code.',\n })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <TextField\n label=\"Listing name\"\n helperText=\"Defaults to the manifest name\"\n value={draft.displayName}\n onChange={(event) => set('displayName')(event.target.value)}\n size=\"small\"\n />\n <TextField\n label=\"Description\"\n helperText=\"The one-line summary on browse cards\"\n value={draft.description}\n onChange={(event) => set('description')(event.target.value)}\n size=\"small\"\n multiline\n minRows={2}\n />\n <TextField\n select\n label=\"Category\"\n value={draft.category}\n onChange={(event) => set('category')(event.target.value)}\n size=\"small\"\n >\n <MenuItem value=\"\">{'None'}</MenuItem>\n {LISTING_CATEGORIES.map((entry) => (\n <MenuItem key={entry} value={entry}>\n {entry}\n </MenuItem>\n ))}\n </TextField>\n {/* The same editor the listing detail page uses (AGL-1080). The\n first time a publisher writes the document reviewers read\n first used to get the worst tool we have, and the second time\n the good one — for the same field. The AGL-969 attestation\n made that worse by asking them to confirm the README\n documents what data the plugin handles, in a box too small to\n see whether it does. */}\n <MarkdownField\n label=\"README\"\n value={draft.readme}\n onChange={set('readme')}\n onPickImageFromMedia={insertReadmeImage}\n editorRef={(handle) => {\n readmeEditorRef.current = handle\n }}\n helperText={\n 'Shown on your listing page — what it does, how to configure ' +\n 'it, and what it reads, stores or sends. Reviewers and ' +\n 'cautious buyers read this first.'\n }\n />\n {/* The only field that exclusively concerns updates (AGL-1080).\n It was a single line labelled `Changelog` and nothing else,\n presented as an equal peer of Category — while AGL-969 attests\n to it and AGL-976 builds the listing's changelog tab out of\n it. The helper names both audiences, because \"what changed\"\n means something different to each. */}\n <TextField\n label=\"Changelog\"\n placeholder=\"Fixed the timer drift on Safari; added a compact layout\"\n helperText={\n isUpdate\n ? 'The field that matters most on an update. Two audiences: ' +\n 'the reviewer, comparing these bytes against ' +\n (liveVersion ? `v${liveVersion}` : 'the last approved ' +\n 'version') +\n ', and every installer deciding whether to move off the ' +\n 'version they are running.'\n : 'Two audiences: the reviewer, comparing this version ' +\n 'against the last approved one, and every installer who ' +\n 'reads it on your listing’s changelog tab before ' +\n 'updating. On a first version there is nothing to compare ' +\n 'against — say what the plugin does instead, or leave it.'\n }\n value={draft.changelog}\n onChange={(event) => set('changelog')(event.target.value)}\n size=\"small\"\n multiline\n minRows={3}\n />\n {/* AGL-1076: the subject of the repository attestation below. */}\n <TextField\n label=\"Repository URL\"\n placeholder=\"https://github.com/acme/widget\"\n required\n error={repositoryInvalid || Boolean(fieldErrors['repositoryUrl'])}\n helperText={\n repositoryInvalid\n ? 'Must be an https URL.'\n : (fieldErrors['repositoryUrl'] ??\n 'The source for this bundle. A reviewer opens it first — ' +\n 'it is what the repository confirmation below is about.')\n }\n value={draft.repositoryUrl}\n onChange={(event) => set('repositoryUrl')(event.target.value)}\n size=\"small\"\n />\n <TextField\n label=\"License\"\n placeholder=\"MIT\"\n helperText=\"Short label, e.g. MIT or Apache-2.0\"\n value={draft.license}\n onChange={(event) => set('license')(event.target.value)}\n size=\"small\"\n />\n </Stack>\n </CardDisplay>\n\n <CardDisplay\n header={'Who can install it, and for how much'}\n help={pluginDocsHelp('publisherHandbook', {\n anchor: '#private-plugins',\n excerpt:\n 'Private is a choice about audience, not about trust — it takes ' +\n 'the identical review path.',\n })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n {/* Private plugins (AGL-968/992): deliberately worded as an\n AUDIENCE choice — private is not a faster lane, and the caption\n says so, because \"skip the queue\" is the reading it would\n otherwise invite. */}\n <TextField\n select\n label=\"Who can install this\"\n value={draft.visibility}\n // Set on FIRST publish only — the server ignores it on a version\n // bump (AGL-968), so offering the choice here would be a control\n // that silently does nothing. Change it from the listing.\n disabled={isUpdate}\n onChange={(event) =>\n set('visibility')(event.target.value as 'public' | 'private')\n }\n size=\"small\"\n helperText={\n isUpdate\n ? 'Set when the listing was created. Change it from the ' +\n 'listing — a new version never changes who can install it.'\n :\n draft.visibility === 'private'\n ? 'Private plugins take the same review path — same ' +\n 'queue, same checklist, same verifier, same signature ' +\n 'for realm trust. They are never listed in the ' +\n 'marketplace, and only your sites can install them.'\n : 'Listed in the marketplace for every workspace once a ' +\n 'reviewer approves it.'\n }\n >\n <MenuItem value=\"public\">\n {'Anyone — publish to the marketplace'}\n </MenuItem>\n <MenuItem value=\"private\">\n {'Only this organization — private plugin'}\n </MenuItem>\n </TextField>\n <TextField\n label=\"Price (USD)\"\n // A private plugin has no buyers, and a price on one would only\n // send the publisher through Stripe onboarding to sell to\n // themselves.\n disabled={draft.visibility === 'private'}\n // The minimum price (AGL-2343), from the same functions every other\n // publish form reads, so no two forms can quote different figures\n // at a publisher — and stated up front rather than only on refusal.\n error={priceBelowFloor}\n helperText={\n draft.visibility === 'private'\n ? 'Private plugins are free — nobody else can install them.'\n : (marketplacePriceCostNote(draft.priceUsd) ??\n marketplacePriceFloorHint(\n 'Paid listings need payouts set up.',\n ))\n }\n type=\"number\"\n value={draft.visibility === 'private' ? '0' : draft.priceUsd}\n onChange={(event) => set('priceUsd')(event.target.value)}\n size=\"small\"\n />\n </Stack>\n </CardDisplay>\n\n {/* The checklist last, in full view of the publish button (AGL-969 /\n AGL-1078). In the dialog it sat below the button and had to be\n scrolled past to be seen at all. Every item is a statement only the\n publisher can make. */}\n <CardDisplay\n header={'Before you publish'}\n help={pluginDocsHelp('publisherHandbook', {\n anchor: '#before-you-publish',\n excerpt:\n 'Recorded against this exact bundle, with your name and the ' +\n 'date, and shown to the reviewer.',\n })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Recorded against this exact bundle, with your name and the ' +\n 'date, and shown to the reviewer. Republishing the same ' +\n 'version number asks again — the bytes changed.'}\n </Typography>\n {missingAttestations.length ? (\n <Alert severity=\"warning\">\n {'This plugin already has a published version, so the ' +\n 'highlighted item applies too.'}\n </Alert>\n ) : null}\n {PUBLISHER_ATTESTATION.map((item) => {\n const flagged = missingAttestations.includes(item.id)\n return (\n <Stack key={item.id} spacing={0.25}>\n <FormControlLabel\n control={\n <Checkbox\n size=\"small\"\n checked={draft.attested.includes(item.id)}\n disabled={busy}\n onChange={() => toggleAttestation(item.id)}\n />\n }\n label={\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', flexWrap: 'wrap' }}\n >\n <Typography\n variant=\"body2\"\n color={flagged ? 'warning.main' : undefined}\n >\n {item.label}\n </Typography>\n {item.updateOnly ? (\n <Chip\n size=\"small\"\n variant=\"outlined\"\n label=\"Updates only\"\n />\n ) : null}\n </Stack>\n }\n />\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ pl: 4 }}\n >\n {item.detail}\n </Typography>\n </Stack>\n )\n })}\n </Stack>\n </CardDisplay>\n\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={() => void submit()}\n disabled={blocked}\n >\n {busy\n ? 'Publishing…'\n : !bundleFile\n ? 'Choose a bundle'\n : unattested.length\n ? `Confirm ${unattested.length} more`\n : unfilledSubjects.length || repositoryInvalid\n ? 'Add a repository URL'\n : isUpdate\n ? 'Publish new version'\n : draft.visibility === 'private'\n ? 'Publish privately'\n : 'Publish plugin'}\n </Button>\n <Button\n disabled={busy}\n onClick={() => {\n clearDraft()\n setDraft(baseline)\n setBundleFile(null)\n setManifestFile(null)\n setDraftRestored(false)\n }}\n >\n {'Discard draft'}\n </Button>\n </Stack>\n\n </Stack>\n )\n}\n\nPublishPluginForm.displayName = 'PublishPluginForm'\n\nexport default PublishPluginForm\n"],"names":["formatMediaRef","isBelowMarketplacePriceFloor","marketplacePriceCostNote","marketplacePriceFloorHint","offeredPluginVersion","useMediaPicker","CardDisplay","useSnackbar","Alert","Button","Checkbox","Chip","FormControlLabel","MenuItem","Stack","TextField","Typography","useRouter","useCallback","useEffect","useRef","useState","useUser","missingAttestationSubjects","PUBLISHER_ATTESTATION","requiredAttestationIds","MarkdownField","pluginDocsHelp","inheritedMediaAlt","authorizedFetch","listingPath","LISTING_CATEGORIES","isHttpsUrl","value","test","trim","length","fileToBase64","file","buffer","arrayBuffer","binary","bytes","Uint8Array","i","String","fromCharCode","btoa","EMPTY_DRAFT","manifestText","displayName","description","category","changelog","priceUsd","readme","license","repositoryUrl","visibility","attested","draftKey","orgId","listingId","readDraft","window","stored","localStorage","getItem","JSON","parse","seedDraft","draft","listing","$id","seeded","carry","key","categories","draftDiffersFrom","baseline","Object","keys","some","undefined","Array","isArray","other","entry","index","PublishPluginForm","props","fieldErrors","basePath","revocation","isUpdate","Boolean","data","user","router","enqueueSnackbar","busy","setBusy","bundleRef","manifestRef","bundleFile","setBundleFile","manifestFile","setManifestFile","readmeEditorRef","pickMedia","insertReadmeImage","media","kind","src","mediaScope","mediaId","url","current","insertImage","assetAlt","alt","setDraft","draftRestored","setDraftRestored","problems","setProblems","missingAttestations","setMissingAttestations","agreementProblem","setAgreementProblem","setFieldErrors","set","setItem","stringify","clearDraft","removeItem","toggleAttestation","id","includes","filter","unattested","unfilledSubjects","repositoryInvalid","typedManifestId","raw","parsed","manifestIdMismatch","pluginId","liveVersion","latestVersion","readManifest","text","submit","bundle","manifest","response","method","headers","body","name","Math","max","round","Number","attestation","payload","json","catch","ok","map","problem","message","level","check","agreement","error","fromEntries","field","variant","status","allowDuplicate","version","push","console","priceBelowFloor","blocked","spacing","severity","component","onClose","header","help","anchor","excerpt","contentGutterX","contentGutterY","input","ref","type","accept","hidden","onChange","event","target","files","direction","sx","alignItems","size","onClick","click","color","noWrap","placeholder","multiline","minRows","fullWidth","helperText","ul","style","margin","paddingLeft","li","label","select","onPickImageFromMedia","editorRef","handle","required","disabled","item","flagged","control","checked","flexWrap","updateOnly","pl","detail"],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Publish a plugin (AGL-1078), at `{marketplace}/publish/plugin`.
|
|
3
|
+
*
|
|
4
|
+
* This was `UploadPluginDialog`, a modal with no URL, no draft and no room.
|
|
5
|
+
* The address exists so a publish can be linked, reloaded, and reached from a
|
|
6
|
+
* listing pre-bound to it with `?listing=` (AGL-1008) — an update is this
|
|
7
|
+
* same form with a listing already chosen.
|
|
8
|
+
*
|
|
9
|
+
* It was a console route until AGL-3080 and is now a page of the marketplace
|
|
10
|
+
* hub's own subtree. The chrome it used to build itself — the trail, the
|
|
11
|
+
* heading, the help `?` — is published upward instead, which is what lets the
|
|
12
|
+
* shell own one layout for every plugin surface.
|
|
13
|
+
*/
|
|
14
|
+
export declare function PublishPluginPage(props: {
|
|
15
|
+
orgId: string;
|
|
16
|
+
basePath: string;
|
|
17
|
+
/**
|
|
18
|
+
* The reader's org permissions, and whether they have settled. Both, never
|
|
19
|
+
* just the map: it fails OPEN and answers as an ADMIN's until the member
|
|
20
|
+
* read lands, so reading it early renders the form for someone who cannot
|
|
21
|
+
* publish and lets them get as far as a 403.
|
|
22
|
+
*/
|
|
23
|
+
permissions?: Record<string, boolean | undefined>;
|
|
24
|
+
permissionsLoaded: boolean;
|
|
25
|
+
}): import("react").JSX.Element;
|
|
26
|
+
export declare namespace PublishPluginPage {
|
|
27
|
+
var displayName: string;
|
|
28
|
+
}
|
|
29
|
+
export default PublishPluginPage;
|
|
@@ -0,0 +1,92 @@
|
|
|
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 { PageHeaderHelp, PageHeaderRecord } from "@aglyn/aglyn";
|
|
19
|
+
import { Alert, Stack } from "@mui/material";
|
|
20
|
+
import { doc } from "firebase/firestore";
|
|
21
|
+
import { useSearchParams } from "next/navigation";
|
|
22
|
+
import { useFirestore, useFirestoreDoc } from "@aglyn/tenant-feature-instance";
|
|
23
|
+
import PublishPluginForm from "./publish-plugin-form.component.js";
|
|
24
|
+
/**
|
|
25
|
+
* Publish a plugin (AGL-1078), at `{marketplace}/publish/plugin`.
|
|
26
|
+
*
|
|
27
|
+
* This was `UploadPluginDialog`, a modal with no URL, no draft and no room.
|
|
28
|
+
* The address exists so a publish can be linked, reloaded, and reached from a
|
|
29
|
+
* listing pre-bound to it with `?listing=` (AGL-1008) — an update is this
|
|
30
|
+
* same form with a listing already chosen.
|
|
31
|
+
*
|
|
32
|
+
* It was a console route until AGL-3080 and is now a page of the marketplace
|
|
33
|
+
* hub's own subtree. The chrome it used to build itself — the trail, the
|
|
34
|
+
* heading, the help `?` — is published upward instead, which is what lets the
|
|
35
|
+
* shell own one layout for every plugin surface.
|
|
36
|
+
*/ export function PublishPluginPage(props) {
|
|
37
|
+
var _ref, _target_displayName;
|
|
38
|
+
const { orgId, basePath, permissions, permissionsLoaded } = props;
|
|
39
|
+
const firestore = useFirestore();
|
|
40
|
+
// `?listing=` binds this to an existing listing (AGL-1008) — the same form,
|
|
41
|
+
// shipping a new version rather than creating a listing. The server still
|
|
42
|
+
// decides which it is, from profileId plus the manifest id; this only
|
|
43
|
+
// pre-fills what the publisher already owns and lets the page say what is
|
|
44
|
+
// about to happen.
|
|
45
|
+
const searchParams = useSearchParams();
|
|
46
|
+
const listingId = String((_ref = searchParams == null ? void 0 : searchParams.get('listing')) != null ? _ref : '');
|
|
47
|
+
const { data: listing } = useFirestoreDoc(()=>doc(firestore, 'marketplaceListings', listingId || '-none-'), [
|
|
48
|
+
firestore,
|
|
49
|
+
listingId
|
|
50
|
+
], {
|
|
51
|
+
idField: '$id'
|
|
52
|
+
});
|
|
53
|
+
// The kill switch on that listing (AGL-2368). The form tells a publisher
|
|
54
|
+
// which version "is what installs today", and it read the review-verdict
|
|
55
|
+
// mirror to say so — which revocation does not clear, so a publisher whose
|
|
56
|
+
// live version had been stopped was told it was still serving customers.
|
|
57
|
+
const { data: revocation } = useFirestoreDoc(()=>listingId ? doc(firestore, 'revocations', listingId) : null, [
|
|
58
|
+
firestore,
|
|
59
|
+
listingId
|
|
60
|
+
]);
|
|
61
|
+
// Only bind to a listing this org actually publishes. Otherwise a guessed
|
|
62
|
+
// id would pre-fill someone else's listing content into this form.
|
|
63
|
+
const target = listingId && (listing == null ? void 0 : listing.profileId) && listing.profileId === orgId ? listing : null;
|
|
64
|
+
const allowed = permissionsLoaded && (permissions == null ? void 0 : permissions['publishToMarketplace']) === true;
|
|
65
|
+
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
66
|
+
children: [
|
|
67
|
+
/*#__PURE__*/ _jsx(PageHeaderRecord, {
|
|
68
|
+
title: target ? `New version of ${(_target_displayName = target.displayName) != null ? _target_displayName : 'your plugin'}` : 'Publish a plugin'
|
|
69
|
+
}),
|
|
70
|
+
/*#__PURE__*/ _jsx(PageHeaderHelp, {
|
|
71
|
+
topic: "publisherHandbook",
|
|
72
|
+
anchor: "#publishing-a-version"
|
|
73
|
+
}),
|
|
74
|
+
!permissionsLoaded ? null : !allowed ? /*#__PURE__*/ _jsx(Stack, {
|
|
75
|
+
spacing: 2,
|
|
76
|
+
children: /*#__PURE__*/ _jsx(Alert, {
|
|
77
|
+
severity: "info",
|
|
78
|
+
children: 'Your organization role does not allow publishing to the ' + 'marketplace.'
|
|
79
|
+
})
|
|
80
|
+
}) : orgId && (!listingId || listing !== undefined) ? /*#__PURE__*/ _jsx(PublishPluginForm, {
|
|
81
|
+
orgId: orgId,
|
|
82
|
+
basePath: basePath,
|
|
83
|
+
listing: target,
|
|
84
|
+
revocation: revocation != null ? revocation : null
|
|
85
|
+
}) : null
|
|
86
|
+
]
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
PublishPluginPage.displayName = 'PublishPluginPage';
|
|
90
|
+
export default PublishPluginPage;
|
|
91
|
+
|
|
92
|
+
//# sourceMappingURL=publish-plugin-page.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/components/publish-plugin-page.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 { PageHeaderHelp, PageHeaderRecord } from '@aglyn/aglyn'\nimport { Alert, Stack } from '@mui/material'\nimport { doc } from 'firebase/firestore'\nimport { useSearchParams } from 'next/navigation'\nimport { useFirestore, useFirestoreDoc } from '@aglyn/tenant-feature-instance'\nimport PublishPluginForm from './publish-plugin-form.component'\n\n/**\n * Publish a plugin (AGL-1078), at `{marketplace}/publish/plugin`.\n *\n * This was `UploadPluginDialog`, a modal with no URL, no draft and no room.\n * The address exists so a publish can be linked, reloaded, and reached from a\n * listing pre-bound to it with `?listing=` (AGL-1008) — an update is this\n * same form with a listing already chosen.\n *\n * It was a console route until AGL-3080 and is now a page of the marketplace\n * hub's own subtree. The chrome it used to build itself — the trail, the\n * heading, the help `?` — is published upward instead, which is what lets the\n * shell own one layout for every plugin surface.\n */\nexport function PublishPluginPage(props: {\n orgId: string\n basePath: string\n /**\n * The reader's org permissions, and whether they have settled. Both, never\n * just the map: it fails OPEN and answers as an ADMIN's until the member\n * read lands, so reading it early renders the form for someone who cannot\n * publish and lets them get as far as a 403.\n */\n permissions?: Record<string, boolean | undefined>\n permissionsLoaded: boolean\n}) {\n const { orgId, basePath, permissions, permissionsLoaded } = props\n const firestore = useFirestore()\n\n // `?listing=` binds this to an existing listing (AGL-1008) — the same form,\n // shipping a new version rather than creating a listing. The server still\n // decides which it is, from profileId plus the manifest id; this only\n // pre-fills what the publisher already owns and lets the page say what is\n // about to happen.\n const searchParams = useSearchParams()\n const listingId = String(searchParams?.get('listing') ?? '')\n const { data: listing } = useFirestoreDoc<any>(\n () => doc(firestore, 'marketplaceListings', listingId || '-none-'),\n [firestore, listingId],\n { idField: '$id' },\n )\n // The kill switch on that listing (AGL-2368). The form tells a publisher\n // which version \"is what installs today\", and it read the review-verdict\n // mirror to say so — which revocation does not clear, so a publisher whose\n // live version had been stopped was told it was still serving customers.\n const { data: revocation } = useFirestoreDoc<any>(\n () => (listingId ? doc(firestore, 'revocations', listingId) : null),\n [firestore, listingId],\n )\n\n // Only bind to a listing this org actually publishes. Otherwise a guessed\n // id would pre-fill someone else's listing content into this form.\n const target =\n listingId && listing?.profileId && listing.profileId === orgId\n ? listing\n : null\n\n const allowed = permissionsLoaded && permissions?.['publishToMarketplace'] === true\n\n return (\n <>\n {/*\n The heading and the last crumb, which the surface's own declaration\n cannot supply: \"Publish a plugin\" and \"New version of X\" are different\n pages at one address, and which one you are on depends on a listing\n this page reads.\n */}\n <PageHeaderRecord\n title={\n target\n ? `New version of ${target.displayName ?? 'your plugin'}`\n : 'Publish a plugin'\n }\n />\n <PageHeaderHelp topic=\"publisherHandbook\" anchor=\"#publishing-a-version\" />\n {!permissionsLoaded ? null : !allowed ? (\n <Stack spacing={2}>\n <Alert severity=\"info\">\n {'Your organization role does not allow publishing to the ' +\n 'marketplace.'}\n </Alert>\n </Stack>\n ) : orgId && (!listingId || listing !== undefined) ? (\n <PublishPluginForm\n orgId={orgId}\n basePath={basePath}\n listing={target}\n revocation={revocation ?? null}\n />\n ) : null}\n </>\n )\n}\n\nPublishPluginPage.displayName = 'PublishPluginPage'\n\nexport default PublishPluginPage\n"],"names":["PageHeaderHelp","PageHeaderRecord","Alert","Stack","doc","useSearchParams","useFirestore","useFirestoreDoc","PublishPluginForm","PublishPluginPage","props","target","orgId","basePath","permissions","permissionsLoaded","firestore","searchParams","listingId","String","get","data","listing","idField","revocation","profileId","allowed","title","displayName","topic","anchor","spacing","severity","undefined"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAASA,cAAc,EAAEC,gBAAgB,QAAQ,eAAc;AAC/D,SAASC,KAAK,EAAEC,KAAK,QAAQ,gBAAe;AAC5C,SAASC,GAAG,QAAQ,qBAAoB;AACxC,SAASC,eAAe,QAAQ,kBAAiB;AACjD,SAASC,YAAY,EAAEC,eAAe,QAAQ,iCAAgC;AAC9E,OAAOC,uBAAuB,qCAAiC;AAE/D;;;;;;;;;;;;CAYC,GACD,OAAO,SAASC,kBAAkBC,KAWjC;cA6C+BC;IA5C9B,MAAM,EAAEC,KAAK,EAAEC,QAAQ,EAAEC,WAAW,EAAEC,iBAAiB,EAAE,GAAGL;IAC5D,MAAMM,YAAYV;IAElB,4EAA4E;IAC5E,0EAA0E;IAC1E,sEAAsE;IACtE,0EAA0E;IAC1E,mBAAmB;IACnB,MAAMW,eAAeZ;IACrB,MAAMa,YAAYC,eAAOF,gCAAAA,aAAcG,GAAG,CAAC,6BAAc;IACzD,MAAM,EAAEC,MAAMC,OAAO,EAAE,GAAGf,gBACxB,IAAMH,IAAIY,WAAW,uBAAuBE,aAAa,WACzD;QAACF;QAAWE;KAAU,EACtB;QAAEK,SAAS;IAAM;IAEnB,yEAAyE;IACzE,yEAAyE;IACzE,2EAA2E;IAC3E,yEAAyE;IACzE,MAAM,EAAEF,MAAMG,UAAU,EAAE,GAAGjB,gBAC3B,IAAOW,YAAYd,IAAIY,WAAW,eAAeE,aAAa,MAC9D;QAACF;QAAWE;KAAU;IAGxB,0EAA0E;IAC1E,mEAAmE;IACnE,MAAMP,SACJO,cAAaI,2BAAAA,QAASG,SAAS,KAAIH,QAAQG,SAAS,KAAKb,QACrDU,UACA;IAEN,MAAMI,UAAUX,qBAAqBD,CAAAA,+BAAAA,WAAa,CAAC,uBAAuB,MAAK;IAE/E,qBACE;;0BAOE,KAACb;gBACC0B,OACEhB,SACI,CAAC,eAAe,GAAEA,sBAAAA,OAAOiB,WAAW,YAAlBjB,sBAAsB,eAAe,GACvD;;0BAGR,KAACX;gBAAe6B,OAAM;gBAAoBC,QAAO;;YAChD,CAACf,oBAAoB,OAAO,CAACW,wBAC5B,KAACvB;gBAAM4B,SAAS;0BACd,cAAA,KAAC7B;oBAAM8B,UAAS;8BACb,6DACC;;iBAGJpB,SAAU,CAAA,CAACM,aAAaI,YAAYW,SAAQ,kBAC9C,KAACzB;gBACCI,OAAOA;gBACPC,UAAUA;gBACVS,SAASX;gBACTa,UAAU,EAAEA,qBAAAA,aAAc;iBAE1B;;;AAGV;AAEAf,kBAAkBmB,WAAW,GAAG;AAEhC,eAAenB,kBAAiB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare function PublisherProfilePage(props: {
|
|
2
|
+
/** The handle (or legacy org id) in the URL, from the shell's segments. */
|
|
3
|
+
segment: string;
|
|
4
|
+
basePath: string;
|
|
5
|
+
/** The site an install acts through — the org's first, as it always was. */
|
|
6
|
+
actingHost: string;
|
|
7
|
+
permissions?: Record<string, boolean | undefined>;
|
|
8
|
+
orgSlug: string;
|
|
9
|
+
}): import("react").JSX.Element;
|
|
10
|
+
export declare namespace PublisherProfilePage {
|
|
11
|
+
var displayName: string;
|
|
12
|
+
}
|
|
13
|
+
export default PublisherProfilePage;
|
|
@@ -0,0 +1,261 @@
|
|
|
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 { mdiEmailOutline, mdiGithub, mdiLifebuoy, mdiLinkedin, mdiTwitter, mdiWeb } from "@aglyn/shared-data-mdi";
|
|
19
|
+
import { pluginDocsHelp, PageHeaderHelp, PageHeaderRecord } from "@aglyn/aglyn";
|
|
20
|
+
import { CardDisplay, MdiIcon } from "@aglyn/shared-ui-jsx";
|
|
21
|
+
import { Alert, Avatar, IconButton, Stack, Tooltip, Typography } from "@mui/material";
|
|
22
|
+
import { collection, doc, limit, query, where } from "firebase/firestore";
|
|
23
|
+
import { useRouter } from "next/navigation";
|
|
24
|
+
import { useEffect } from "react";
|
|
25
|
+
import { useFirestore, useFirestoreCollection, useFirestoreDoc } from "@aglyn/tenant-feature-instance";
|
|
26
|
+
import MarketplaceBrowse from "./marketplace-browse.component.js";
|
|
27
|
+
import { publisherPath } from "../model/marketplace-paths.js";
|
|
28
|
+
/**
|
|
29
|
+
* Org-scope publisher storefront (AGL-869): every marketplace listing from one
|
|
30
|
+
* publisher, reached from a listing's Publisher card or a browse card's
|
|
31
|
+
* "by @handle" link. The body is the marketplace plugin's browse widget filtered
|
|
32
|
+
* to this publisher — the app stays plugin-free and reuses one grid.
|
|
33
|
+
*/ // Render-time https guard (AGL-1009), mirroring the marketplace model's
|
|
34
|
+
// `safePublisherHref` — the console can't import across the aglyn:addons
|
|
35
|
+
// boundary. The save route only ever stores https URLs, but this renderer
|
|
36
|
+
// must not trust stored data it did not write: a `javascript:` URL that
|
|
37
|
+
// somehow reached the doc renders as nothing, not as a link.
|
|
38
|
+
/**
|
|
39
|
+
* `publisherHandles/{handle}` — the marketplace's handle reservations, and
|
|
40
|
+
* since AGL-2312 the rename tombstones this page follows.
|
|
41
|
+
*
|
|
42
|
+
* Spelled out rather than imported for the same reason `safeHref` is: the
|
|
43
|
+
* console cannot cross the `aglyn:addons` boundary. The collection is a
|
|
44
|
+
* public read (`allow read: if true`), so a client lookup is legitimate here.
|
|
45
|
+
*/ const PUBLISHER_HANDLES = 'publisherHandles';
|
|
46
|
+
const safeHref = (url)=>typeof url === 'string' && /^https:\/\//i.test(url) && url.length <= 500 ? url : undefined;
|
|
47
|
+
export function PublisherProfilePage(props) {
|
|
48
|
+
var _, _ref, _ref1, _ref2;
|
|
49
|
+
const { segment, basePath, actingHost, permissions, orgSlug } = props;
|
|
50
|
+
const firestore = useFirestore();
|
|
51
|
+
// The URL carries the publisher's HANDLE (AGL-1001) — the identity they
|
|
52
|
+
// chose and the one shown on every browse card — not the opaque org
|
|
53
|
+
// document id it used to. Both resolve: the handle query runs first, and
|
|
54
|
+
// an id read backs it up so links already in the wild keep working. A
|
|
55
|
+
// handle is `^[a-z0-9][a-z0-9-]{2,29}$`, which an id can also match, so
|
|
56
|
+
// neither lookup can be skipped on the shape of the segment alone.
|
|
57
|
+
const { data: byHandle, status: byHandleStatus } = useFirestoreCollection(()=>query(collection(firestore, 'publisherProfiles'), where('handle', '==', segment || '-missing-'), limit(1)), [
|
|
58
|
+
firestore,
|
|
59
|
+
segment
|
|
60
|
+
], {
|
|
61
|
+
idField: '$id'
|
|
62
|
+
});
|
|
63
|
+
const { data: byId, status: byIdStatus } = useFirestoreDoc(()=>doc(firestore, 'publisherProfiles', segment || '-missing-'), [
|
|
64
|
+
firestore,
|
|
65
|
+
segment
|
|
66
|
+
], {
|
|
67
|
+
idField: '$id'
|
|
68
|
+
});
|
|
69
|
+
const profile = (_ = (byHandle != null ? byHandle : [])[0]) != null ? _ : byId;
|
|
70
|
+
const profileId = String((_ref = profile == null ? void 0 : profile.$id) != null ? _ref : '');
|
|
71
|
+
/**
|
|
72
|
+
* A RENAMED HANDLE STILL RESOLVES (AGL-2312).
|
|
73
|
+
*
|
|
74
|
+
* `claimPublisherHandle` leaves `{ orgId, movedTo, renamedAt }` on the old
|
|
75
|
+
* handle when a publisher renames, and says why: *"so old marketplace links
|
|
76
|
+
* can still resolve"*. Nothing read it. A publisher who renamed silently
|
|
77
|
+
* broke every existing link to their storefront — SEO and referral traffic
|
|
78
|
+
* to PAID listings — and the page rendered the bare title `'Publisher'`.
|
|
79
|
+
*
|
|
80
|
+
* The org-slug tombstone written identically in `organizations.ts` IS read:
|
|
81
|
+
* `/api/orgs/slug-verdict` feeds `middleware.ts`, which issues the redirect.
|
|
82
|
+
* That asymmetry is what makes this an omission rather than a decision, and
|
|
83
|
+
* this is the same move one layer in — client-side because the handle lives
|
|
84
|
+
* in a path segment the middleware does not resolve, and because
|
|
85
|
+
* `publisherHandles` is a public read.
|
|
86
|
+
*
|
|
87
|
+
* `redirect` rather than `push`: the old URL is not a place to come back to.
|
|
88
|
+
*/ const router = useRouter();
|
|
89
|
+
const { data: handleTombstone, status: tombstoneStatus } = useFirestoreDoc(()=>doc(firestore, PUBLISHER_HANDLES, segment || '-missing-'), [
|
|
90
|
+
firestore,
|
|
91
|
+
segment
|
|
92
|
+
]);
|
|
93
|
+
/**
|
|
94
|
+
* Only once BOTH profile lookups have settled to nothing.
|
|
95
|
+
*
|
|
96
|
+
* A tombstone that arrives before the profile does must not bounce a handle
|
|
97
|
+
* that resolves: re-claiming a handle replaces the reservation wholesale and
|
|
98
|
+
* clears `movedTo`, so the two states cannot coexist for long — but they can
|
|
99
|
+
* for one render, and a redirect fired on a half-loaded page is a redirect
|
|
100
|
+
* nobody can reproduce.
|
|
101
|
+
*/ const movedTo = !profile && byHandleStatus === 'success' && byIdStatus !== 'loading' && tombstoneStatus === 'success' ? String((_ref1 = handleTombstone == null ? void 0 : handleTombstone['movedTo']) != null ? _ref1 : '') : '';
|
|
102
|
+
useEffect(()=>{
|
|
103
|
+
if (!movedTo || movedTo === segment) return;
|
|
104
|
+
router.replace(publisherPath(basePath, movedTo));
|
|
105
|
+
}, [
|
|
106
|
+
movedTo,
|
|
107
|
+
segment,
|
|
108
|
+
basePath,
|
|
109
|
+
router
|
|
110
|
+
]);
|
|
111
|
+
const title = (_ref2 = profile == null ? void 0 : profile.displayName) != null ? _ref2 : (profile == null ? void 0 : profile.handle) ? `@${profile.handle}` : 'Publisher';
|
|
112
|
+
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
113
|
+
children: [
|
|
114
|
+
/*#__PURE__*/ _jsx(PageHeaderRecord, {
|
|
115
|
+
title: title
|
|
116
|
+
}),
|
|
117
|
+
/*#__PURE__*/ _jsx(PageHeaderHelp, {
|
|
118
|
+
topic: "publishAPlugin",
|
|
119
|
+
anchor: "#your-publisher-profile"
|
|
120
|
+
}),
|
|
121
|
+
/*#__PURE__*/ _jsxs(Stack, {
|
|
122
|
+
spacing: 2,
|
|
123
|
+
children: [
|
|
124
|
+
/*#__PURE__*/ _jsx(CardDisplay, {
|
|
125
|
+
header: title,
|
|
126
|
+
help: pluginDocsHelp('publisherHandbook', {
|
|
127
|
+
excerpt: 'The public face of a publisher — the profile customers read before ' + 'deciding to trust a listing.'
|
|
128
|
+
}),
|
|
129
|
+
contentGutterX: true,
|
|
130
|
+
contentGutterY: true,
|
|
131
|
+
children: /*#__PURE__*/ _jsxs(Stack, {
|
|
132
|
+
direction: "row",
|
|
133
|
+
spacing: 2,
|
|
134
|
+
children: [
|
|
135
|
+
/*#__PURE__*/ _jsx(Avatar, {
|
|
136
|
+
src: safeHref(profile == null ? void 0 : profile.avatarUrl),
|
|
137
|
+
alt: title,
|
|
138
|
+
variant: "rounded",
|
|
139
|
+
sx: {
|
|
140
|
+
width: 64,
|
|
141
|
+
height: 64
|
|
142
|
+
},
|
|
143
|
+
children: String(title).slice(0, 1).toUpperCase()
|
|
144
|
+
}),
|
|
145
|
+
/*#__PURE__*/ _jsxs(Stack, {
|
|
146
|
+
spacing: 0.5,
|
|
147
|
+
sx: {
|
|
148
|
+
minWidth: 0,
|
|
149
|
+
flex: 1
|
|
150
|
+
},
|
|
151
|
+
children: [
|
|
152
|
+
(profile == null ? void 0 : profile.handle) ? /*#__PURE__*/ _jsx(Typography, {
|
|
153
|
+
variant: "body2",
|
|
154
|
+
color: "text.secondary",
|
|
155
|
+
children: `@${profile.handle}`
|
|
156
|
+
}) : null,
|
|
157
|
+
(profile == null ? void 0 : profile.bio) ? /*#__PURE__*/ _jsx(Typography, {
|
|
158
|
+
variant: "body2",
|
|
159
|
+
children: profile.bio
|
|
160
|
+
}) : /*#__PURE__*/ _jsx(Typography, {
|
|
161
|
+
variant: "body2",
|
|
162
|
+
color: "text.secondary",
|
|
163
|
+
children: 'Everything this publisher has shared to the marketplace.'
|
|
164
|
+
}),
|
|
165
|
+
(()=>{
|
|
166
|
+
const links = [
|
|
167
|
+
{
|
|
168
|
+
key: 'website',
|
|
169
|
+
label: 'Website',
|
|
170
|
+
icon: mdiWeb.path,
|
|
171
|
+
href: safeHref(profile == null ? void 0 : profile.website)
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
key: 'support',
|
|
175
|
+
label: 'Support',
|
|
176
|
+
icon: mdiLifebuoy.path,
|
|
177
|
+
href: safeHref(profile == null ? void 0 : profile.supportUrl)
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
key: 'github',
|
|
181
|
+
label: 'GitHub',
|
|
182
|
+
icon: mdiGithub.path,
|
|
183
|
+
href: safeHref(profile == null ? void 0 : profile.githubUrl)
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
key: 'x',
|
|
187
|
+
label: 'X',
|
|
188
|
+
icon: mdiTwitter.path,
|
|
189
|
+
href: safeHref(profile == null ? void 0 : profile.xUrl)
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
key: 'linkedin',
|
|
193
|
+
label: 'LinkedIn',
|
|
194
|
+
icon: mdiLinkedin.path,
|
|
195
|
+
href: safeHref(profile == null ? void 0 : profile.linkedinUrl)
|
|
196
|
+
}
|
|
197
|
+
].filter((link)=>link.href);
|
|
198
|
+
const email = typeof (profile == null ? void 0 : profile.supportEmail) === 'string' && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(profile.supportEmail) ? profile.supportEmail : undefined;
|
|
199
|
+
if (!links.length && !email) return null;
|
|
200
|
+
return /*#__PURE__*/ _jsxs(Stack, {
|
|
201
|
+
direction: "row",
|
|
202
|
+
spacing: 0.5,
|
|
203
|
+
sx: {
|
|
204
|
+
mt: 0.5
|
|
205
|
+
},
|
|
206
|
+
children: [
|
|
207
|
+
email ? /*#__PURE__*/ _jsx(Tooltip, {
|
|
208
|
+
title: `Support: ${email}`,
|
|
209
|
+
children: /*#__PURE__*/ _jsx(IconButton, {
|
|
210
|
+
size: "small",
|
|
211
|
+
component: "a",
|
|
212
|
+
href: `mailto:${email}`,
|
|
213
|
+
"aria-label": `Email support at ${email}`,
|
|
214
|
+
children: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
215
|
+
path: mdiEmailOutline.path,
|
|
216
|
+
fontSize: "small"
|
|
217
|
+
})
|
|
218
|
+
})
|
|
219
|
+
}) : null,
|
|
220
|
+
links.map((link)=>/*#__PURE__*/ _jsx(Tooltip, {
|
|
221
|
+
title: link.label,
|
|
222
|
+
children: /*#__PURE__*/ _jsx(IconButton, {
|
|
223
|
+
size: "small",
|
|
224
|
+
component: "a",
|
|
225
|
+
href: link.href,
|
|
226
|
+
target: "_blank",
|
|
227
|
+
rel: "noopener noreferrer",
|
|
228
|
+
"aria-label": link.label,
|
|
229
|
+
children: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
230
|
+
path: link.icon,
|
|
231
|
+
fontSize: "small"
|
|
232
|
+
})
|
|
233
|
+
})
|
|
234
|
+
}, link.key))
|
|
235
|
+
]
|
|
236
|
+
});
|
|
237
|
+
})()
|
|
238
|
+
]
|
|
239
|
+
})
|
|
240
|
+
]
|
|
241
|
+
})
|
|
242
|
+
}),
|
|
243
|
+
actingHost ? /*#__PURE__*/ _jsx(MarketplaceBrowse, {
|
|
244
|
+
hostId: actingHost,
|
|
245
|
+
permissions: permissions,
|
|
246
|
+
orgScoped: true,
|
|
247
|
+
orgSlug: orgSlug,
|
|
248
|
+
publisherId: profileId
|
|
249
|
+
}) : /*#__PURE__*/ _jsx(Alert, {
|
|
250
|
+
severity: "info",
|
|
251
|
+
children: 'Add a site to your organization to browse and install ' + 'marketplace items.'
|
|
252
|
+
})
|
|
253
|
+
]
|
|
254
|
+
})
|
|
255
|
+
]
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
PublisherProfilePage.displayName = 'PublisherProfilePage';
|
|
259
|
+
export default PublisherProfilePage;
|
|
260
|
+
|
|
261
|
+
//# sourceMappingURL=publisher-profile-page.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/components/publisher-profile-page.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n mdiEmailOutline,\n mdiGithub,\n mdiLifebuoy,\n mdiLinkedin,\n mdiStorefrontOutline,\n mdiTwitter,\n mdiWeb,\n} from '@aglyn/shared-data-mdi'\nimport { pluginDocsHelp, PageHeaderHelp, PageHeaderRecord } from '@aglyn/aglyn'\nimport { CardDisplay, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { Alert, Avatar, IconButton, Stack, Tooltip, Typography } from '@mui/material'\nimport { collection, doc, limit, query, where } from 'firebase/firestore'\nimport { useRouter } from 'next/navigation'\nimport { useEffect } from 'react'\nimport {\n useFirestore,\n useFirestoreCollection,\n useFirestoreDoc,\n} from '@aglyn/tenant-feature-instance'\nimport MarketplaceBrowse from './marketplace-browse.component'\nimport { publisherPath } from '../model/marketplace-paths'\n\n/**\n * Org-scope publisher storefront (AGL-869): every marketplace listing from one\n * publisher, reached from a listing's Publisher card or a browse card's\n * \"by @handle\" link. The body is the marketplace plugin's browse widget filtered\n * to this publisher — the app stays plugin-free and reuses one grid.\n */\n// Render-time https guard (AGL-1009), mirroring the marketplace model's\n// `safePublisherHref` — the console can't import across the aglyn:addons\n// boundary. The save route only ever stores https URLs, but this renderer\n// must not trust stored data it did not write: a `javascript:` URL that\n// somehow reached the doc renders as nothing, not as a link.\n/**\n * `publisherHandles/{handle}` — the marketplace's handle reservations, and\n * since AGL-2312 the rename tombstones this page follows.\n *\n * Spelled out rather than imported for the same reason `safeHref` is: the\n * console cannot cross the `aglyn:addons` boundary. The collection is a\n * public read (`allow read: if true`), so a client lookup is legitimate here.\n */\nconst PUBLISHER_HANDLES = 'publisherHandles'\n\nconst safeHref = (url: unknown): string | undefined =>\n typeof url === 'string' && /^https:\\/\\//i.test(url) && url.length <= 500\n ? url\n : undefined\n\nexport function PublisherProfilePage(props: {\n /** The handle (or legacy org id) in the URL, from the shell's segments. */\n segment: string\n basePath: string\n /** The site an install acts through — the org's first, as it always was. */\n actingHost: string\n permissions?: Record<string, boolean | undefined>\n orgSlug: string\n}) {\n const { segment, basePath, actingHost, permissions, orgSlug } = props\n const firestore = useFirestore()\n\n // The URL carries the publisher's HANDLE (AGL-1001) — the identity they\n // chose and the one shown on every browse card — not the opaque org\n // document id it used to. Both resolve: the handle query runs first, and\n // an id read backs it up so links already in the wild keep working. A\n // handle is `^[a-z0-9][a-z0-9-]{2,29}$`, which an id can also match, so\n // neither lookup can be skipped on the shape of the segment alone.\n const { data: byHandle, status: byHandleStatus } = useFirestoreCollection<any>(\n () =>\n query(\n collection(firestore, 'publisherProfiles'),\n where('handle', '==', segment || '-missing-'),\n limit(1),\n ),\n [firestore, segment],\n { idField: '$id' },\n )\n const { data: byId, status: byIdStatus } = useFirestoreDoc<any>(\n () => doc(firestore, 'publisherProfiles', segment || '-missing-'),\n [firestore, segment],\n { idField: '$id' },\n )\n const profile = (byHandle ?? [])[0] ?? byId\n const profileId = String(profile?.$id ?? '')\n\n /**\n * A RENAMED HANDLE STILL RESOLVES (AGL-2312).\n *\n * `claimPublisherHandle` leaves `{ orgId, movedTo, renamedAt }` on the old\n * handle when a publisher renames, and says why: *\"so old marketplace links\n * can still resolve\"*. Nothing read it. A publisher who renamed silently\n * broke every existing link to their storefront — SEO and referral traffic\n * to PAID listings — and the page rendered the bare title `'Publisher'`.\n *\n * The org-slug tombstone written identically in `organizations.ts` IS read:\n * `/api/orgs/slug-verdict` feeds `middleware.ts`, which issues the redirect.\n * That asymmetry is what makes this an omission rather than a decision, and\n * this is the same move one layer in — client-side because the handle lives\n * in a path segment the middleware does not resolve, and because\n * `publisherHandles` is a public read.\n *\n * `redirect` rather than `push`: the old URL is not a place to come back to.\n */\n const router = useRouter()\n const { data: handleTombstone, status: tombstoneStatus } =\n useFirestoreDoc<any>(\n () => doc(firestore, PUBLISHER_HANDLES, segment || '-missing-'),\n [firestore, segment],\n )\n /**\n * Only once BOTH profile lookups have settled to nothing.\n *\n * A tombstone that arrives before the profile does must not bounce a handle\n * that resolves: re-claiming a handle replaces the reservation wholesale and\n * clears `movedTo`, so the two states cannot coexist for long — but they can\n * for one render, and a redirect fired on a half-loaded page is a redirect\n * nobody can reproduce.\n */\n const movedTo =\n !profile &&\n byHandleStatus === 'success' &&\n byIdStatus !== 'loading' &&\n tombstoneStatus === 'success'\n ? String(handleTombstone?.['movedTo'] ?? '')\n : ''\n useEffect(() => {\n if (!movedTo || movedTo === segment) return\n router.replace(publisherPath(basePath, movedTo))\n }, [movedTo, segment, basePath, router])\n\n const title = profile?.displayName ?? (profile?.handle ? `@${profile.handle}` : 'Publisher')\n\n return (\n <>\n {/*\n The publisher's own name in the heading and the trail (AGL-1000/1001)\n — the surface's declaration says \"Marketplace\", which reads identically\n on every publisher's page, and a trail that repeats the page type tells\n you nothing the hero above it did not already say.\n */}\n <PageHeaderRecord title={title} />\n <PageHeaderHelp topic=\"publishAPlugin\" anchor=\"#your-publisher-profile\" />\n <Stack spacing={2}>\n <CardDisplay header={title}\n help={pluginDocsHelp('publisherHandbook', {\n excerpt:\n 'The public face of a publisher — the profile customers read before ' +\n 'deciding to trust a listing.',\n })} contentGutterX contentGutterY>\n <Stack direction=\"row\" spacing={2}>\n {/* Logo (AGL-1009) — only an https URL is ever emitted; a\n profile without one falls back to the initial. */}\n <Avatar\n src={safeHref(profile?.avatarUrl)}\n alt={title}\n variant=\"rounded\"\n sx={{ width: 64, height: 64 }}\n >\n {String(title).slice(0, 1).toUpperCase()}\n </Avatar>\n <Stack spacing={0.5} sx={{ minWidth: 0, flex: 1 }}>\n {profile?.handle ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`@${profile.handle}`}\n </Typography>\n ) : null}\n {profile?.bio ? (\n <Typography variant=\"body2\">{profile.bio}</Typography>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Everything this publisher has shared to the marketplace.'}\n </Typography>\n )}\n {/* Contact and external links (AGL-1009): a fixed icon row,\n each guarded https-only at render as well as at write. */}\n {(() => {\n const links = [\n {\n key: 'website',\n label: 'Website',\n icon: mdiWeb.path,\n href: safeHref(profile?.website),\n },\n {\n key: 'support',\n label: 'Support',\n icon: mdiLifebuoy.path,\n href: safeHref(profile?.supportUrl),\n },\n {\n key: 'github',\n label: 'GitHub',\n icon: mdiGithub.path,\n href: safeHref(profile?.githubUrl),\n },\n {\n key: 'x',\n label: 'X',\n icon: mdiTwitter.path,\n href: safeHref(profile?.xUrl),\n },\n {\n key: 'linkedin',\n label: 'LinkedIn',\n icon: mdiLinkedin.path,\n href: safeHref(profile?.linkedinUrl),\n },\n ].filter((link) => link.href)\n const email =\n typeof profile?.supportEmail === 'string' &&\n /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(profile.supportEmail)\n ? profile.supportEmail\n : undefined\n if (!links.length && !email) return null\n return (\n <Stack direction=\"row\" spacing={0.5} sx={{ mt: 0.5 }}>\n {email ? (\n <Tooltip title={`Support: ${email}`}>\n <IconButton\n size=\"small\"\n component=\"a\"\n href={`mailto:${email}`}\n aria-label={`Email support at ${email}`}\n >\n <MdiIcon\n path={mdiEmailOutline.path}\n fontSize=\"small\"\n />\n </IconButton>\n </Tooltip>\n ) : null}\n {links.map((link) => (\n <Tooltip key={link.key} title={link.label}>\n <IconButton\n size=\"small\"\n component=\"a\"\n href={link.href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n aria-label={link.label}\n >\n <MdiIcon path={link.icon} fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n ))}\n </Stack>\n )\n })()}\n </Stack>\n </Stack>\n </CardDisplay>\n {/* The same grid browse uses, filtered to this publisher —\n rendered directly rather than through the `orgMarketplace` zone.\n That zone exists so a console page can show the marketplace\n without importing this plugin; here the plugin is already the\n thing rendering. */}\n {actingHost ? (\n <MarketplaceBrowse\n hostId={actingHost}\n permissions={permissions}\n orgScoped\n orgSlug={orgSlug}\n publisherId={profileId}\n />\n ) : (\n <Alert severity=\"info\">\n {'Add a site to your organization to browse and install ' +\n 'marketplace items.'}\n </Alert>\n )}\n </Stack>\n </>\n )\n}\nPublisherProfilePage.displayName = 'PublisherProfilePage'\n\nexport default PublisherProfilePage\n"],"names":["mdiEmailOutline","mdiGithub","mdiLifebuoy","mdiLinkedin","mdiTwitter","mdiWeb","pluginDocsHelp","PageHeaderHelp","PageHeaderRecord","CardDisplay","MdiIcon","Alert","Avatar","IconButton","Stack","Tooltip","Typography","collection","doc","limit","query","where","useRouter","useEffect","useFirestore","useFirestoreCollection","useFirestoreDoc","MarketplaceBrowse","publisherPath","PUBLISHER_HANDLES","safeHref","url","test","length","undefined","PublisherProfilePage","props","segment","basePath","actingHost","permissions","orgSlug","firestore","data","byHandle","status","byHandleStatus","idField","byId","byIdStatus","profile","profileId","String","$id","router","handleTombstone","tombstoneStatus","movedTo","replace","title","displayName","handle","topic","anchor","spacing","header","help","excerpt","contentGutterX","contentGutterY","direction","src","avatarUrl","alt","variant","sx","width","height","slice","toUpperCase","minWidth","flex","color","bio","links","key","label","icon","path","href","website","supportUrl","githubUrl","xUrl","linkedinUrl","filter","link","email","supportEmail","mt","size","component","aria-label","fontSize","map","target","rel","hostId","orgScoped","publisherId","severity"],"mappings":"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"}
|