@aglyn/aglyn 1.0.0-beta.205 → 1.0.0-beta.207
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 +11 -11
- package/src/lib/app-utils/console-routes.d.ts +14 -0
- package/src/lib/app-utils/console-routes.js +11 -0
- package/src/lib/app-utils/console-routes.js.map +1 -1
- package/src/lib/app-utils/content-authors.d.ts +12 -64
- package/src/lib/app-utils/content-authors.js +0 -133
- package/src/lib/app-utils/content-authors.js.map +1 -1
- package/src/lib/app-utils/docs-help.generated.d.ts +6 -0
- package/src/lib/app-utils/docs-help.generated.js +61 -0
- package/src/lib/app-utils/docs-help.generated.js.map +1 -1
- package/src/lib/app-utils/docs-index.generated.js +10 -3
- package/src/lib/app-utils/docs-index.generated.js.map +1 -1
- package/src/lib/app-utils/health-report.js +13 -0
- package/src/lib/app-utils/health-report.js.map +1 -1
- package/src/lib/app-utils/host-transfer.d.ts +55 -0
- package/src/lib/app-utils/host-transfer.js +23 -0
- package/src/lib/app-utils/host-transfer.js.map +1 -0
- package/src/lib/app-utils/local-business.d.ts +161 -0
- package/src/lib/app-utils/local-business.js +253 -0
- package/src/lib/app-utils/local-business.js.map +1 -0
- package/src/lib/app-utils/notifications.d.ts +1 -13
- package/src/lib/app-utils/notifications.js +1 -16
- package/src/lib/app-utils/notifications.js.map +1 -1
- package/src/lib/app-utils/operator-alerts.d.ts +132 -0
- package/src/lib/app-utils/operator-alerts.js +513 -0
- package/src/lib/app-utils/operator-alerts.js.map +1 -0
- package/src/lib/app-utils/server.d.ts +1 -0
- package/src/lib/app-utils/server.js +9 -0
- package/src/lib/app-utils/server.js.map +1 -1
- package/src/lib/app-utils/site-entity-json-ld.d.ts +96 -0
- package/src/lib/app-utils/site-entity-json-ld.js +220 -0
- package/src/lib/app-utils/site-entity-json-ld.js.map +1 -0
- package/src/lib/foundation/definitions/platform.types.d.ts +24 -0
- package/src/lib/foundation/definitions/platform.types.js.map +1 -1
- package/src/lib/plugin-manager/feature-plugins.d.ts +9 -0
- package/src/lib/plugin-manager/feature-plugins.js +9 -0
- package/src/lib/plugin-manager/feature-plugins.js.map +1 -1
- package/src/lib/plugin-manager/first-party-plugins.generated.js +8 -1
- package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
- package/src/lib/plugin-manager/operator-alerts.d.ts +37 -0
- package/src/lib/plugin-manager/operator-alerts.js +80 -0
- package/src/lib/plugin-manager/operator-alerts.js.map +1 -0
- package/src/lib/plugin-manager/plugin-host-collections.d.ts +11 -0
- package/src/lib/plugin-manager/plugin-host-collections.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/first-party-plugins.generated.ts"],"sourcesContent":["/**\n * GENERATED FILE — do not edit. Regenerate with:\n * node tools/scripts/generate-plugin-manifests.mjs\n *\n * The switchboard catalog (AGL-3080): one row per plugin and capability, each\n * declared by its own `catalog` block in plugins.config.json. The core holds\n * the types and the resolvers in `enabled-plugins.ts`; it holds no row.\n */\n\nimport type { FirstPartyPlugin, PluginEditBarLink, PublishedSiteImpact } from './enabled-plugins'\nimport type { ResolvedPluginHostCollection, ResolvedPluginOrgCollection } from './plugin-host-collections'\nimport type { ResolvedPluginOrgCapacity } from './plugin-org-capacity'\n\nexport const FIRST_PARTY_PLUGINS: readonly FirstPartyPlugin[] = [\n {\n \"id\": \"mui\",\n \"label\": \"Components\",\n \"alwaysOn\": true,\n \"description\": \"The base component and theme library every site builds on.\"\n },\n {\n \"id\": \"forms\",\n \"label\": \"Forms\",\n \"alwaysOnForWorkspace\": true,\n \"description\": \"Forms on the site and the catalog that owns them.\",\n \"siteOff\": {\n \"stops\": \"Switching Forms off for this site stops forms rendering on its published pages, refuses every submission sent to it, and blocks publishing a form, or a page that carries one, until Forms is back on.\",\n \"keeps\": \"Submissions already received, the workspace’s form catalog and the CRM leads its forms created are kept, and forms keep working on the workspace’s other sites.\",\n \"confirm\": true,\n \"pages\": {\n \"heading\": \"These published pages carry a form. Their forms stop rendering and stop accepting submissions:\",\n \"none\": \"No published page on this site carries a form.\"\n }\n }\n },\n {\n \"id\": \"ai\",\n \"label\": \"AI\",\n \"alwaysOnForWorkspace\": true,\n \"description\": \"The assistant, generative building and automation, and the AI add-on.\",\n \"siteOff\": {\n \"stops\": \"Switching AI off for this site hides the assistant, Describe it, the AI cards and the editor’s AI controls on this site, refuses every AI request made for it, and stops its queued AI jobs without spending credits.\",\n \"keeps\": \"It does not stop the workspace’s AI add-on, credits, allotments or overage billing, and AI keeps working on the workspace’s other sites.\"\n }\n },\n {\n \"id\": \"accounts\",\n \"label\": \"User Accounts\",\n \"description\": \"Visitor accounts on the site: the /signin, /signup and /recover pages, and the Members blocks. Off for a site until you turn it on.\",\n \"releaseFlag\": \"release_member_accounts\",\n \"defaultOffPerSite\": true,\n \"requires\": [\n \"commerce\"\n ]\n },\n {\n \"id\": \"bookings\",\n \"label\": \"Bookings\",\n \"description\": \"Services, open slots, and paid bookings.\",\n \"releaseFlag\": \"release_bookings\"\n },\n {\n \"id\": \"commerce\",\n \"label\": \"Commerce\",\n \"description\": \"Products, carts, checkout, orders, POS.\",\n \"releaseFlag\": \"release_commerce_v2\"\n },\n {\n \"id\": \"marketplace\",\n \"label\": \"Marketplace\",\n \"description\": \"Marketplace listings, templates, and installs.\",\n \"releaseFlag\": \"release_marketplace\"\n },\n {\n \"id\": \"crm\",\n \"label\": \"CRM\",\n \"description\": \"Leads, contacts, companies, deals, tasks and reports.\",\n \"releaseFlag\": \"release_crm\"\n },\n {\n \"id\": \"outreach\",\n \"label\": \"Sequences\",\n \"description\": \"One-to-one email sequences sent from connected mailboxes.\",\n \"releaseFlag\": \"release_outreach\"\n },\n {\n \"id\": \"data\",\n \"label\": \"Data\",\n \"description\": \"Datasets, records, and CSV import/export.\",\n \"releaseFlag\": \"release_data_store\"\n },\n {\n \"id\": \"email\",\n \"label\": \"Email\",\n \"description\": \"Designed emails and campaign sending.\",\n \"releaseFlag\": \"release_email\"\n },\n {\n \"id\": \"events-calendar\",\n \"label\": \"Events Calendar\",\n \"description\": \"Event lists and calendars.\",\n \"releaseFlag\": \"release_events\"\n },\n {\n \"id\": \"inbox\",\n \"label\": \"Inbox\",\n \"description\": \"Form submissions and lead inbox.\",\n \"releaseFlag\": \"release_inbox\"\n },\n {\n \"id\": \"logic\",\n \"label\": \"Logic\",\n \"description\": \"Variables, functions, and reference health.\",\n \"releaseFlag\": \"release_logic\"\n },\n {\n \"id\": \"marketing\",\n \"label\": \"Marketing\",\n \"description\": \"Overlays, campaigns, and experiments.\",\n \"releaseFlag\": \"release_marketing\"\n },\n {\n \"id\": \"redirects\",\n \"label\": \"Redirects\",\n \"description\": \"URL redirect rules.\",\n \"releaseFlag\": \"release_redirects\"\n },\n {\n \"id\": \"workflows\",\n \"label\": \"Automation\",\n \"description\": \"Workflows, actions, webhooks, and run logs.\",\n \"releaseFlag\": \"release_workflows\"\n },\n]\n\nexport const PUBLISHED_SITE_IMPACT: Readonly<Record<string, PublishedSiteImpact>> = {\n \"mui\": \"elements\",\n \"forms\": \"elements\",\n \"ai\": \"console-only\",\n \"accounts\": \"routes\",\n \"bookings\": \"elements\",\n \"commerce\": \"elements\",\n \"marketplace\": \"console-only\",\n \"crm\": \"console-only\",\n \"outreach\": \"console-only\",\n \"data\": \"console-only\",\n \"email\": \"elements\",\n \"events-calendar\": \"elements\",\n \"inbox\": \"console-only\",\n \"logic\": \"console-only\",\n \"marketing\": \"elements\",\n \"redirects\": \"routes\",\n \"workflows\": \"routes\",\n}\n\n/**\n * The admin edit bar's quick links, in the order they are drawn — each\n * declared by the plugin whose console page it opens (AGL-3080).\n */\nexport const PLUGIN_EDIT_BAR_LINKS: readonly PluginEditBarLink[] = [\n {\n \"pluginId\": \"inbox\",\n \"order\": 20,\n \"label\": \"Inbox\",\n \"path\": \"/inbox\"\n },\n {\n \"pluginId\": \"commerce\",\n \"order\": 30,\n \"label\": \"Orders\",\n \"path\": \"/products/orders\"\n },\n]\n\n/**\n * Every host subcollection a first-party plugin owns, declared by that plugin\n * (AGL-3080). Core's readers ask this list; core names no collection.\n */\nexport const PLUGIN_HOST_COLLECTIONS_DECLARED: readonly ResolvedPluginHostCollection[] = [\n {\n \"pluginId\": \"forms\",\n \"name\": \"forms\",\n \"routeSlug\": \"forms\"\n },\n {\n \"pluginId\": \"bookings\",\n \"name\": \"services\",\n \"routeSlug\": \"bookings\"\n },\n {\n \"pluginId\": \"bookings\",\n \"name\": \"bookings\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Booking records — a customer, a time and a service pointer. The service holds the imagery and IS scanned; a booking is the transaction against it.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"products\",\n \"routeSlug\": \"products\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"productCategories\",\n \"routeSlug\": \"products\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"suppliers\",\n \"routeSlug\": \"products\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"locations\",\n \"routeSlug\": \"products\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"coupons\",\n \"routeSlug\": \"products\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"discounts\",\n \"routeSlug\": \"products\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"reviews\",\n \"routeSlug\": \"products\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"memberPosts\",\n \"routeSlug\": \"products\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"resources\",\n \"routeSlug\": \"products\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"settings\",\n \"label\": \"Store settings\",\n \"routeSlug\": \"products\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"siteMembers\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"siteMemberCredentials\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"A site member's password hash, one document per member, read and written only by the membership routes (AGL-3308). A credential with no content field, and no client may read it.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"orders\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Completed orders. Line items COPY a product's `imageUrl` at purchase time, so these would match — and matching would be the wrong answer: an order is an immutable record of what was sold, deleting the asset changes nothing about it, and no author can edit one. A busy store also holds more of these than the whole read budget.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"carts\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Live and abandoned carts, with the same copied line-item image and the same reasoning, at higher volume — one document per shopper session.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"checkouts\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"In-flight checkout sessions. Transient buyer state carrying the same line-item snapshot as `carts`.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"reservations\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"POS and booking holds. Short-lived transaction rows pointing at a resource that is itself scanned.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"stockHolds\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Inventory holds taken during checkout. Machine-written, short-lived, and a quantity rather than content.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"subscriptions\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Site membership subscriptions: a plan pointer, a status and Stripe ids. Server-written from the billing webhook.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"registers\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"POS register allocations — a count against the register add-on, read by billing (AGL-1775). No content field at all.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"giftCards\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Gift card balances and redemption history. Money, not content.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"licenseKeys\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Digital-product license keys issued at fulfilment: a code, an order pointer and a revocation flag.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"inventoryAdjustments\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"The append-only stock adjustment ledger (AGL-2269). One row per manual stock edit and per cancellation release, unbounded over a store's life.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"inventoryReconciliation\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Reconciliation runs comparing counted stock against recorded stock. Machine-written totals.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"restockAlerts\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Back-in-stock requests: an email address and a product pointer, written by visitors.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"stripeTaxRates\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Cached Stripe tax rate ids, written by the tax sync. Vendor ids.\"\n },\n {\n \"pluginId\": \"marketplace\",\n \"name\": \"installs\"\n },\n {\n \"pluginId\": \"crm\",\n \"name\": \"leads\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Captured leads, on the same footing as `formSubmissions`: visitor-submitted, unbounded, and never a place an author places an asset.\"\n },\n {\n \"pluginId\": \"email\",\n \"name\": \"suppressions\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Unsubscribes, bounces and spam complaints. Email addresses and a reason.\"\n },\n {\n \"pluginId\": \"events-calendar\",\n \"name\": \"events\",\n \"routeSlug\": \"events\"\n },\n {\n \"pluginId\": \"inbox\",\n \"name\": \"formSubmissions\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Visitor form submissions. Unbounded, PII-heavy, and a file attached to one is the visitor's upload — not a library asset an author picked, and not something deleting a library asset would break.\"\n },\n {\n \"pluginId\": \"logic\",\n \"name\": \"functions\",\n \"routeSlug\": \"logic\"\n },\n {\n \"pluginId\": \"logic\",\n \"name\": \"variables\",\n \"routeSlug\": \"logic\"\n },\n {\n \"pluginId\": \"marketing\",\n \"name\": \"experiments\",\n \"routeSlug\": \"marketing\"\n },\n {\n \"pluginId\": \"marketing\",\n \"name\": \"overlays\",\n \"routeSlug\": \"marketing\"\n },\n {\n \"pluginId\": \"marketing\",\n \"name\": \"campaignAttributions\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"One row per conversion — a form submission, a lead, a contact or a booking — recording which campaign the visitor arrived from. Written only by the Admin SDK, and the whole record is three sanitized UTM strings capped at a hundred characters: there is no field an asset could occupy and no surface that could pick one into it. Unbounded on the same footing as the conversions it credits.\"\n },\n {\n \"pluginId\": \"redirects\",\n \"name\": \"redirects\",\n \"routeSlug\": \"redirects\"\n },\n {\n \"pluginId\": \"workflows\",\n \"name\": \"workflows\",\n \"routeSlug\": \"automation\"\n },\n {\n \"pluginId\": \"workflows\",\n \"name\": \"webhooks\",\n \"routeSlug\": \"automation\"\n },\n {\n \"pluginId\": \"workflows\",\n \"name\": \"actions\",\n \"routeSlug\": \"automation\"\n },\n]\n\n/**\n * Every org collection a first-party plugin owns whose documents the media\n * scan reads, declared by that plugin (AGL-3273).\n */\nexport const PLUGIN_ORG_COLLECTIONS_DECLARED: readonly ResolvedPluginOrgCollection[] = [\n {\n \"pluginId\": \"marketing\",\n \"name\": \"campaigns\",\n \"routeSlug\": \"marketing\",\n \"siteField\": \"hostId\"\n },\n {\n \"pluginId\": \"marketing\",\n \"name\": \"emailCampaigns\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"The campaign CONTAINER — a name, a date window, the list ids it is aimed at, a topic and the sites it runs on. Distinct from `campaigns`, which holds the sends and IS scanned because a send carries the copy that went out. A container carries no copy and no asset reference: the design a send renders is a screen, and the screen is where the picker writes.\"\n },\n {\n \"pluginId\": \"workflows\",\n \"name\": \"automations\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"An org automation (AGL-3302) — a trigger, its conditions, the sites it is placed on, and a step list drawn from the server steps only: email subject and body as plain text, list, dataset and campaign ids, CRM fields and waits. No step it may hold carries a media reference, the in-page steps that could (custom HTML, popups) are refused by its save route, and it is written only by that route.\"\n },\n]\n\n/**\n * Every org capacity a first-party plugin backs, declared by that plugin\n * (AGL-3080). Core owns the money; this says what is counted and what it is\n * called.\n */\nexport const PLUGIN_ORG_CAPACITIES_DECLARED: readonly ResolvedPluginOrgCapacity[] = [\n {\n \"pluginId\": \"data\",\n \"kind\": \"datasets\",\n \"order\": 30,\n \"collection\": \"datasets\",\n \"addonKind\": \"datasets\",\n \"includedEntitlement\": \"datasetsPerOrg\",\n \"purchaseCeilingEntitlement\": \"maxDatasetsPerOrg\",\n \"nouns\": {\n \"one\": \"dataset\",\n \"many\": \"datasets\",\n \"addon\": \"extra datasets\"\n }\n },\n]\n"],"names":["FIRST_PARTY_PLUGINS","PUBLISHED_SITE_IMPACT","PLUGIN_EDIT_BAR_LINKS","PLUGIN_HOST_COLLECTIONS_DECLARED","PLUGIN_ORG_COLLECTIONS_DECLARED","PLUGIN_ORG_CAPACITIES_DECLARED"],"mappings":"AAAA;;;;;;;CAOC,GAMD,OAAO,MAAMA,sBAAmD;IAC9D;QACE,MAAM;QACN,SAAS;QACT,YAAY;QACZ,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,wBAAwB;QACxB,eAAe;QACf,WAAW;YACT,SAAS;YACT,SAAS;YACT,WAAW;YACX,SAAS;gBACP,WAAW;gBACX,QAAQ;YACV;QACF;IACF;IACA;QACE,MAAM;QACN,SAAS;QACT,wBAAwB;QACxB,eAAe;QACf,WAAW;YACT,SAAS;YACT,SAAS;QACX;IACF;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;QACf,qBAAqB;QACrB,YAAY;YACV;SACD;IACH;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;CACD,CAAA;AAED,OAAO,MAAMC,wBAAuE;IAClF,OAAO;IACP,SAAS;IACT,MAAM;IACN,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,eAAe;IACf,OAAO;IACP,YAAY;IACZ,QAAQ;IACR,S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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/first-party-plugins.generated.ts"],"sourcesContent":["/**\n * GENERATED FILE — do not edit. Regenerate with:\n * node tools/scripts/generate-plugin-manifests.mjs\n *\n * The switchboard catalog (AGL-3080): one row per plugin and capability, each\n * declared by its own `catalog` block in plugins.config.json. The core holds\n * the types and the resolvers in `enabled-plugins.ts`; it holds no row.\n */\n\nimport type { FirstPartyPlugin, PluginEditBarLink, PublishedSiteImpact } from './enabled-plugins'\nimport type { ResolvedPluginHostCollection, ResolvedPluginOrgCollection } from './plugin-host-collections'\nimport type { ResolvedPluginOrgCapacity } from './plugin-org-capacity'\n\nexport const FIRST_PARTY_PLUGINS: readonly FirstPartyPlugin[] = [\n {\n \"id\": \"mui\",\n \"label\": \"Components\",\n \"alwaysOn\": true,\n \"description\": \"The base component and theme library every site builds on.\"\n },\n {\n \"id\": \"forms\",\n \"label\": \"Forms\",\n \"alwaysOnForWorkspace\": true,\n \"description\": \"Forms on the site and the catalog that owns them.\",\n \"siteOff\": {\n \"stops\": \"Switching Forms off for this site stops forms rendering on its published pages, refuses every submission sent to it, and blocks publishing a form, or a page that carries one, until Forms is back on.\",\n \"keeps\": \"Submissions already received, the workspace’s form catalog and the CRM leads its forms created are kept, and forms keep working on the workspace’s other sites.\",\n \"confirm\": true,\n \"pages\": {\n \"heading\": \"These published pages carry a form. Their forms stop rendering and stop accepting submissions:\",\n \"none\": \"No published page on this site carries a form.\"\n }\n }\n },\n {\n \"id\": \"ai\",\n \"label\": \"AI\",\n \"alwaysOnForWorkspace\": true,\n \"description\": \"The assistant, generative building and automation, and the AI add-on.\",\n \"siteOff\": {\n \"stops\": \"Switching AI off for this site hides the assistant, Describe it, the AI cards and the editor’s AI controls on this site, refuses every AI request made for it, and stops its queued AI jobs without spending credits.\",\n \"keeps\": \"It does not stop the workspace’s AI add-on, credits, allotments or overage billing, and AI keeps working on the workspace’s other sites.\"\n }\n },\n {\n \"id\": \"accounts\",\n \"label\": \"User Accounts\",\n \"description\": \"Visitor accounts on the site: the /signin, /signup and /recover pages, and the Members blocks. Off for a site until you turn it on.\",\n \"releaseFlag\": \"release_member_accounts\",\n \"defaultOffPerSite\": true,\n \"requires\": [\n \"commerce\"\n ]\n },\n {\n \"id\": \"bookings\",\n \"label\": \"Bookings\",\n \"description\": \"Services, open slots, and paid bookings.\",\n \"releaseFlag\": \"release_bookings\"\n },\n {\n \"id\": \"commerce\",\n \"label\": \"Commerce\",\n \"description\": \"Products, carts, checkout, orders, POS.\",\n \"releaseFlag\": \"release_commerce_v2\"\n },\n {\n \"id\": \"marketplace\",\n \"label\": \"Marketplace\",\n \"description\": \"Marketplace listings, templates, and installs.\",\n \"releaseFlag\": \"release_marketplace\"\n },\n {\n \"id\": \"crm\",\n \"label\": \"CRM\",\n \"description\": \"Leads, contacts, companies, deals, tasks and reports.\",\n \"releaseFlag\": \"release_crm\"\n },\n {\n \"id\": \"outreach\",\n \"label\": \"Sequences\",\n \"description\": \"One-to-one email sequences sent from connected mailboxes.\",\n \"releaseFlag\": \"release_outreach\"\n },\n {\n \"id\": \"data\",\n \"label\": \"Data\",\n \"description\": \"Datasets, records, and CSV import/export.\",\n \"releaseFlag\": \"release_data_store\"\n },\n {\n \"id\": \"email\",\n \"label\": \"Email\",\n \"description\": \"Designed emails and campaign sending.\",\n \"releaseFlag\": \"release_email\"\n },\n {\n \"id\": \"events-calendar\",\n \"label\": \"Events Calendar\",\n \"description\": \"Event lists and calendars.\",\n \"releaseFlag\": \"release_events\"\n },\n {\n \"id\": \"inbox\",\n \"label\": \"Inbox\",\n \"description\": \"Form submissions and lead inbox.\",\n \"releaseFlag\": \"release_inbox\"\n },\n {\n \"id\": \"logic\",\n \"label\": \"Logic\",\n \"description\": \"Variables, functions, and reference health.\",\n \"releaseFlag\": \"release_logic\"\n },\n {\n \"id\": \"marketing\",\n \"label\": \"Marketing\",\n \"description\": \"Overlays, campaigns, and experiments.\",\n \"releaseFlag\": \"release_marketing\"\n },\n {\n \"id\": \"redirects\",\n \"label\": \"Redirects\",\n \"description\": \"URL redirect rules.\",\n \"releaseFlag\": \"release_redirects\"\n },\n {\n \"id\": \"workflows\",\n \"label\": \"Automation\",\n \"description\": \"Workflows, actions, webhooks, and run logs.\",\n \"releaseFlag\": \"release_workflows\"\n },\n]\n\nexport const PUBLISHED_SITE_IMPACT: Readonly<Record<string, PublishedSiteImpact>> = {\n \"mui\": \"elements\",\n \"forms\": \"elements\",\n \"ai\": \"console-only\",\n \"accounts\": \"routes\",\n \"bookings\": \"elements\",\n \"commerce\": \"elements\",\n \"marketplace\": \"console-only\",\n \"crm\": \"console-only\",\n \"outreach\": \"console-only\",\n \"data\": \"console-only\",\n \"email\": \"elements\",\n \"events-calendar\": \"elements\",\n \"inbox\": \"console-only\",\n \"logic\": \"console-only\",\n \"marketing\": \"elements\",\n \"redirects\": \"routes\",\n \"workflows\": \"routes\",\n}\n\n/**\n * The admin edit bar's quick links, in the order they are drawn — each\n * declared by the plugin whose console page it opens (AGL-3080).\n */\nexport const PLUGIN_EDIT_BAR_LINKS: readonly PluginEditBarLink[] = [\n {\n \"pluginId\": \"inbox\",\n \"order\": 20,\n \"label\": \"Inbox\",\n \"path\": \"/inbox\"\n },\n {\n \"pluginId\": \"commerce\",\n \"order\": 30,\n \"label\": \"Orders\",\n \"path\": \"/products/orders\"\n },\n]\n\n/**\n * Every host subcollection a first-party plugin owns, declared by that plugin\n * (AGL-3080). Core's readers ask this list; core names no collection.\n */\nexport const PLUGIN_HOST_COLLECTIONS_DECLARED: readonly ResolvedPluginHostCollection[] = [\n {\n \"pluginId\": \"forms\",\n \"name\": \"forms\",\n \"routeSlug\": \"forms\"\n },\n {\n \"pluginId\": \"bookings\",\n \"name\": \"services\",\n \"routeSlug\": \"bookings\"\n },\n {\n \"pluginId\": \"bookings\",\n \"name\": \"bookings\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Booking records — a customer, a time and a service pointer. The service holds the imagery and IS scanned; a booking is the transaction against it.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"products\",\n \"routeSlug\": \"products\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"productCategories\",\n \"routeSlug\": \"products\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"suppliers\",\n \"routeSlug\": \"products\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"locations\",\n \"routeSlug\": \"products\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"coupons\",\n \"routeSlug\": \"products\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"discounts\",\n \"routeSlug\": \"products\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"reviews\",\n \"routeSlug\": \"products\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"memberPosts\",\n \"routeSlug\": \"products\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"resources\",\n \"routeSlug\": \"products\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"settings\",\n \"label\": \"Store settings\",\n \"routeSlug\": \"products\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"siteMembers\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"siteMemberCredentials\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"A site member's password hash, one document per member, read and written only by the membership routes (AGL-3308). A credential with no content field, and no client may read it.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"orders\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Completed orders. Line items COPY a product's `imageUrl` at purchase time, so these would match — and matching would be the wrong answer: an order is an immutable record of what was sold, deleting the asset changes nothing about it, and no author can edit one. A busy store also holds more of these than the whole read budget.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"carts\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Live and abandoned carts, with the same copied line-item image and the same reasoning, at higher volume — one document per shopper session.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"checkouts\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"In-flight checkout sessions. Transient buyer state carrying the same line-item snapshot as `carts`.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"reservations\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"POS and booking holds. Short-lived transaction rows pointing at a resource that is itself scanned.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"stockHolds\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Inventory holds taken during checkout. Machine-written, short-lived, and a quantity rather than content.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"subscriptions\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Site membership subscriptions: a plan pointer, a status and Stripe ids. Server-written from the billing webhook.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"registers\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"POS register allocations — a count against the register add-on, read by billing (AGL-1775). No content field at all.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"giftCards\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Gift card balances and redemption history. Money, not content.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"licenseKeys\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Digital-product license keys issued at fulfilment: a code, an order pointer and a revocation flag.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"inventoryAdjustments\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"The append-only stock adjustment ledger (AGL-2269). One row per manual stock edit and per cancellation release, unbounded over a store's life.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"inventoryReconciliation\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Reconciliation runs comparing counted stock against recorded stock. Machine-written totals.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"restockAlerts\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Back-in-stock requests: an email address and a product pointer, written by visitors.\"\n },\n {\n \"pluginId\": \"commerce\",\n \"name\": \"stripeTaxRates\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Cached Stripe tax rate ids, written by the tax sync. Vendor ids.\"\n },\n {\n \"pluginId\": \"marketplace\",\n \"name\": \"installs\"\n },\n {\n \"pluginId\": \"crm\",\n \"name\": \"leads\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Captured leads, on the same footing as `formSubmissions`: visitor-submitted, unbounded, and never a place an author places an asset.\"\n },\n {\n \"pluginId\": \"email\",\n \"name\": \"suppressions\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Unsubscribes, bounces and spam complaints. Email addresses and a reason.\"\n },\n {\n \"pluginId\": \"events-calendar\",\n \"name\": \"events\",\n \"routeSlug\": \"events\"\n },\n {\n \"pluginId\": \"inbox\",\n \"name\": \"formSubmissions\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"Visitor form submissions. Unbounded, PII-heavy, and a file attached to one is the visitor's upload — not a library asset an author picked, and not something deleting a library asset would break.\"\n },\n {\n \"pluginId\": \"logic\",\n \"name\": \"functions\",\n \"routeSlug\": \"logic\"\n },\n {\n \"pluginId\": \"logic\",\n \"name\": \"variables\",\n \"routeSlug\": \"logic\"\n },\n {\n \"pluginId\": \"marketing\",\n \"name\": \"experiments\",\n \"routeSlug\": \"marketing\"\n },\n {\n \"pluginId\": \"marketing\",\n \"name\": \"overlays\",\n \"routeSlug\": \"marketing\"\n },\n {\n \"pluginId\": \"marketing\",\n \"name\": \"campaignAttributions\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"One row per conversion — a form submission, a lead, a contact or a booking — recording which campaign the visitor arrived from. Written only by the Admin SDK, and the whole record is three sanitized UTM strings capped at a hundred characters: there is no field an asset could occupy and no surface that could pick one into it. Unbounded on the same footing as the conversions it credits.\"\n },\n {\n \"pluginId\": \"redirects\",\n \"name\": \"redirects\",\n \"routeSlug\": \"redirects\"\n },\n {\n \"pluginId\": \"workflows\",\n \"name\": \"workflows\",\n \"routeSlug\": \"automation\"\n },\n {\n \"pluginId\": \"workflows\",\n \"name\": \"webhooks\",\n \"routeSlug\": \"automation\"\n },\n {\n \"pluginId\": \"workflows\",\n \"name\": \"actions\",\n \"routeSlug\": \"automation\"\n },\n]\n\n/**\n * Every org collection a first-party plugin owns whose documents the media\n * scan reads, declared by that plugin (AGL-3273).\n */\nexport const PLUGIN_ORG_COLLECTIONS_DECLARED: readonly ResolvedPluginOrgCollection[] = [\n {\n \"pluginId\": \"marketing\",\n \"name\": \"campaigns\",\n \"routeSlug\": \"marketing\",\n \"siteField\": \"hostId\",\n \"holdsTransferWhile\": {\n \"field\": \"status\",\n \"values\": [\n \"scheduled\",\n \"sending\"\n ]\n }\n },\n {\n \"pluginId\": \"marketing\",\n \"name\": \"emailCampaigns\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"The campaign CONTAINER — a name, a date window, the list ids it is aimed at, a topic and the sites it runs on. Distinct from `campaigns`, which holds the sends and IS scanned because a send carries the copy that went out. A container carries no copy and no asset reference: the design a send renders is a screen, and the screen is where the picker writes.\"\n },\n {\n \"pluginId\": \"workflows\",\n \"name\": \"automations\",\n \"mediaScan\": \"none\",\n \"mediaScanReason\": \"An org automation (AGL-3302) — a trigger, its conditions, the sites it is placed on, and a step list drawn from the server steps only: email subject and body as plain text, list, dataset and campaign ids, CRM fields and waits. No step it may hold carries a media reference, the in-page steps that could (custom HTML, popups) are refused by its save route, and it is written only by that route.\"\n },\n]\n\n/**\n * Every org capacity a first-party plugin backs, declared by that plugin\n * (AGL-3080). Core owns the money; this says what is counted and what it is\n * called.\n */\nexport const PLUGIN_ORG_CAPACITIES_DECLARED: readonly ResolvedPluginOrgCapacity[] = [\n {\n \"pluginId\": \"data\",\n \"kind\": \"datasets\",\n \"order\": 30,\n \"collection\": \"datasets\",\n \"addonKind\": \"datasets\",\n \"includedEntitlement\": \"datasetsPerOrg\",\n \"purchaseCeilingEntitlement\": \"maxDatasetsPerOrg\",\n \"nouns\": {\n \"one\": \"dataset\",\n \"many\": \"datasets\",\n \"addon\": \"extra datasets\"\n }\n },\n]\n"],"names":["FIRST_PARTY_PLUGINS","PUBLISHED_SITE_IMPACT","PLUGIN_EDIT_BAR_LINKS","PLUGIN_HOST_COLLECTIONS_DECLARED","PLUGIN_ORG_COLLECTIONS_DECLARED","PLUGIN_ORG_CAPACITIES_DECLARED"],"mappings":"AAAA;;;;;;;CAOC,GAMD,OAAO,MAAMA,sBAAmD;IAC9D;QACE,MAAM;QACN,SAAS;QACT,YAAY;QACZ,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,wBAAwB;QACxB,eAAe;QACf,WAAW;YACT,SAAS;YACT,SAAS;YACT,WAAW;YACX,SAAS;gBACP,WAAW;gBACX,QAAQ;YACV;QACF;IACF;IACA;QACE,MAAM;QACN,SAAS;QACT,wBAAwB;QACxB,eAAe;QACf,WAAW;YACT,SAAS;YACT,SAAS;QACX;IACF;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;QACf,qBAAqB;QACrB,YAAY;YACV;SACD;IACH;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;IACA;QACE,MAAM;QACN,SAAS;QACT,eAAe;QACf,eAAe;IACjB;CACD,CAAA;AAED,OAAO,MAAMC,wBAAuE;IAClF,OAAO;IACP,SAAS;IACT,MAAM;IACN,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,eAAe;IACf,OAAO;IACP,YAAY;IACZ,QAAQ;IACR,S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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
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 OperatorAlertDefinition } from '../app-utils/operator-alerts';
|
|
18
|
+
/**
|
|
19
|
+
* Registers a plugin's alert types. Throws on a type outside the plugin's
|
|
20
|
+
* namespace or one core already owns: both are a programming error the
|
|
21
|
+
* plugin's own tests should meet, not something to discover in production.
|
|
22
|
+
*/
|
|
23
|
+
export declare function registerOperatorAlerts(definitions: readonly OperatorAlertDefinition[], options: {
|
|
24
|
+
pluginId: string;
|
|
25
|
+
}): void;
|
|
26
|
+
/** Every alert type: core's, then each plugin's in type order. */
|
|
27
|
+
export declare function listOperatorAlerts(): OperatorAlertDefinition[];
|
|
28
|
+
/** One alert type, core's or a registered plugin's. */
|
|
29
|
+
export declare function getOperatorAlert(type: string): OperatorAlertDefinition | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* The alert a staff notification of this type is, if any: the registry
|
|
32
|
+
* entry that names it as its `notificationType`. How `notifyStaff` routes a
|
|
33
|
+
* notification it was handed into the alert pipeline.
|
|
34
|
+
*/
|
|
35
|
+
export declare function operatorAlertForNotificationType(notificationType: string): OperatorAlertDefinition | undefined;
|
|
36
|
+
/** Test seam: forget every plugin's registration. */
|
|
37
|
+
export declare function resetOperatorAlertsForTests(): void;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ import { CORE_OPERATOR_ALERTS } from "../app-utils/operator-alerts.js";
|
|
18
|
+
/**
|
|
19
|
+
* Operator alerts a PLUGIN contributes (AGL-3377), beside core's own
|
|
20
|
+
* `CORE_OPERATOR_ALERTS`.
|
|
21
|
+
*
|
|
22
|
+
* A plugin declares its alert types here, from its server declarations, in
|
|
23
|
+
* the same shape core uses, and raises them through `raiseOperatorAlert`.
|
|
24
|
+
* Core lists no plugin's types, so a plugin that is not installed adds
|
|
25
|
+
* nothing to Staff → Operator alerts and a new plugin needs no core change.
|
|
26
|
+
*
|
|
27
|
+
* A plugin's types must start with its own id and a dot, which is what keeps
|
|
28
|
+
* two plugins, or a plugin and core, from claiming one settings row.
|
|
29
|
+
* Registering the same type again replaces it in place, so a declarations
|
|
30
|
+
* module evaluated twice registers once.
|
|
31
|
+
*/ const registrations = new Map();
|
|
32
|
+
const CORE_TYPES = new Set(CORE_OPERATOR_ALERTS.map((entry)=>entry.type));
|
|
33
|
+
/**
|
|
34
|
+
* Registers a plugin's alert types. Throws on a type outside the plugin's
|
|
35
|
+
* namespace or one core already owns: both are a programming error the
|
|
36
|
+
* plugin's own tests should meet, not something to discover in production.
|
|
37
|
+
*/ export function registerOperatorAlerts(definitions, options) {
|
|
38
|
+
var _ref;
|
|
39
|
+
const pluginId = String((_ref = options == null ? void 0 : options.pluginId) != null ? _ref : '').trim();
|
|
40
|
+
if (!pluginId) throw new Error('operator alerts need a pluginId');
|
|
41
|
+
for (const definition of definitions){
|
|
42
|
+
var _ref1;
|
|
43
|
+
const type = String((_ref1 = definition == null ? void 0 : definition.type) != null ? _ref1 : '').trim();
|
|
44
|
+
if (!type.startsWith(`${pluginId}.`) || type.length <= pluginId.length + 1) {
|
|
45
|
+
throw new Error(`operator alert "${type}" must be named "${pluginId}.<name>" by the plugin that registers it`);
|
|
46
|
+
}
|
|
47
|
+
if (CORE_TYPES.has(type)) {
|
|
48
|
+
throw new Error(`operator alert "${type}" is core's`);
|
|
49
|
+
}
|
|
50
|
+
registrations.set(type, _extends({}, definition, {
|
|
51
|
+
type,
|
|
52
|
+
pluginId
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** Every alert type: core's, then each plugin's in type order. */ export function listOperatorAlerts() {
|
|
57
|
+
const contributed = [
|
|
58
|
+
...registrations.values()
|
|
59
|
+
].sort((a, b)=>a.type < b.type ? -1 : a.type > b.type ? 1 : 0);
|
|
60
|
+
return [
|
|
61
|
+
...CORE_OPERATOR_ALERTS,
|
|
62
|
+
...contributed
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
/** One alert type, core's or a registered plugin's. */ export function getOperatorAlert(type) {
|
|
66
|
+
var _CORE_OPERATOR_ALERTS_find;
|
|
67
|
+
return (_CORE_OPERATOR_ALERTS_find = CORE_OPERATOR_ALERTS.find((entry)=>entry.type === type)) != null ? _CORE_OPERATOR_ALERTS_find : registrations.get(type);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The alert a staff notification of this type is, if any: the registry
|
|
71
|
+
* entry that names it as its `notificationType`. How `notifyStaff` routes a
|
|
72
|
+
* notification it was handed into the alert pipeline.
|
|
73
|
+
*/ export function operatorAlertForNotificationType(notificationType) {
|
|
74
|
+
return listOperatorAlerts().find((entry)=>entry.notificationType === notificationType && entry.type === notificationType);
|
|
75
|
+
}
|
|
76
|
+
/** Test seam: forget every plugin's registration. */ export function resetOperatorAlertsForTests() {
|
|
77
|
+
registrations.clear();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
//# sourceMappingURL=operator-alerts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/operator-alerts.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n CORE_OPERATOR_ALERTS,\n type OperatorAlertDefinition,\n} from '../app-utils/operator-alerts'\n\n/**\n * Operator alerts a PLUGIN contributes (AGL-3377), beside core's own\n * `CORE_OPERATOR_ALERTS`.\n *\n * A plugin declares its alert types here, from its server declarations, in\n * the same shape core uses, and raises them through `raiseOperatorAlert`.\n * Core lists no plugin's types, so a plugin that is not installed adds\n * nothing to Staff → Operator alerts and a new plugin needs no core change.\n *\n * A plugin's types must start with its own id and a dot, which is what keeps\n * two plugins, or a plugin and core, from claiming one settings row.\n * Registering the same type again replaces it in place, so a declarations\n * module evaluated twice registers once.\n */\n\nconst registrations = new Map<string, OperatorAlertDefinition>()\n\nconst CORE_TYPES = new Set(CORE_OPERATOR_ALERTS.map((entry) => entry.type))\n\n/**\n * Registers a plugin's alert types. Throws on a type outside the plugin's\n * namespace or one core already owns: both are a programming error the\n * plugin's own tests should meet, not something to discover in production.\n */\nexport function registerOperatorAlerts(\n definitions: readonly OperatorAlertDefinition[],\n options: { pluginId: string },\n): void {\n const pluginId = String(options?.pluginId ?? '').trim()\n if (!pluginId) throw new Error('operator alerts need a pluginId')\n for (const definition of definitions) {\n const type = String(definition?.type ?? '').trim()\n if (!type.startsWith(`${pluginId}.`) || type.length <= pluginId.length + 1) {\n throw new Error(\n `operator alert \"${type}\" must be named \"${pluginId}.<name>\" by the plugin that registers it`,\n )\n }\n if (CORE_TYPES.has(type)) {\n throw new Error(`operator alert \"${type}\" is core's`)\n }\n registrations.set(type, { ...definition, type, pluginId })\n }\n}\n\n/** Every alert type: core's, then each plugin's in type order. */\nexport function listOperatorAlerts(): OperatorAlertDefinition[] {\n const contributed = [...registrations.values()].sort((a, b) =>\n a.type < b.type ? -1 : a.type > b.type ? 1 : 0,\n )\n return [...CORE_OPERATOR_ALERTS, ...contributed]\n}\n\n/** One alert type, core's or a registered plugin's. */\nexport function getOperatorAlert(\n type: string,\n): OperatorAlertDefinition | undefined {\n return (\n CORE_OPERATOR_ALERTS.find((entry) => entry.type === type) ??\n registrations.get(type)\n )\n}\n\n/**\n * The alert a staff notification of this type is, if any: the registry\n * entry that names it as its `notificationType`. How `notifyStaff` routes a\n * notification it was handed into the alert pipeline.\n */\nexport function operatorAlertForNotificationType(\n notificationType: string,\n): OperatorAlertDefinition | undefined {\n return listOperatorAlerts().find(\n (entry) => entry.notificationType === notificationType && entry.type === notificationType,\n )\n}\n\n/** Test seam: forget every plugin's registration. */\nexport function resetOperatorAlertsForTests(): void {\n registrations.clear()\n}\n"],"names":["CORE_OPERATOR_ALERTS","registrations","Map","CORE_TYPES","Set","map","entry","type","registerOperatorAlerts","definitions","options","pluginId","String","trim","Error","definition","startsWith","length","has","set","listOperatorAlerts","contributed","values","sort","a","b","getOperatorAlert","find","get","operatorAlertForNotificationType","notificationType","resetOperatorAlertsForTests","clear"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,oBAAoB,QAEf,kCAA8B;AAErC;;;;;;;;;;;;;CAaC,GAED,MAAMC,gBAAgB,IAAIC;AAE1B,MAAMC,aAAa,IAAIC,IAAIJ,qBAAqBK,GAAG,CAAC,CAACC,QAAUA,MAAMC,IAAI;AAEzE;;;;CAIC,GACD,OAAO,SAASC,uBACdC,WAA+C,EAC/CC,OAA6B;;IAE7B,MAAMC,WAAWC,eAAOF,2BAAAA,QAASC,QAAQ,mBAAI,IAAIE,IAAI;IACrD,IAAI,CAACF,UAAU,MAAM,IAAIG,MAAM;IAC/B,KAAK,MAAMC,cAAcN,YAAa;;QACpC,MAAMF,OAAOK,gBAAOG,8BAAAA,WAAYR,IAAI,oBAAI,IAAIM,IAAI;QAChD,IAAI,CAACN,KAAKS,UAAU,CAAC,GAAGL,SAAS,CAAC,CAAC,KAAKJ,KAAKU,MAAM,IAAIN,SAASM,MAAM,GAAG,GAAG;YAC1E,MAAM,IAAIH,MACR,CAAC,gBAAgB,EAAEP,KAAK,iBAAiB,EAAEI,SAAS,wCAAwC,CAAC;QAEjG;QACA,IAAIR,WAAWe,GAAG,CAACX,OAAO;YACxB,MAAM,IAAIO,MAAM,CAAC,gBAAgB,EAAEP,KAAK,WAAW,CAAC;QACtD;QACAN,cAAckB,GAAG,CAACZ,MAAM,aAAKQ;YAAYR;YAAMI;;IACjD;AACF;AAEA,gEAAgE,GAChE,OAAO,SAASS;IACd,MAAMC,cAAc;WAAIpB,cAAcqB,MAAM;KAAG,CAACC,IAAI,CAAC,CAACC,GAAGC,IACvDD,EAAEjB,IAAI,GAAGkB,EAAElB,IAAI,GAAG,CAAC,IAAIiB,EAAEjB,IAAI,GAAGkB,EAAElB,IAAI,GAAG,IAAI;IAE/C,OAAO;WAAIP;WAAyBqB;KAAY;AAClD;AAEA,qDAAqD,GACrD,OAAO,SAASK,iBACdnB,IAAY;QAGVP;IADF,QACEA,6BAAAA,qBAAqB2B,IAAI,CAAC,CAACrB,QAAUA,MAAMC,IAAI,KAAKA,iBAApDP,6BACAC,cAAc2B,GAAG,CAACrB;AAEtB;AAEA;;;;CAIC,GACD,OAAO,SAASsB,iCACdC,gBAAwB;IAExB,OAAOV,qBAAqBO,IAAI,CAC9B,CAACrB,QAAUA,MAAMwB,gBAAgB,KAAKA,oBAAoBxB,MAAMC,IAAI,KAAKuB;AAE7E;AAEA,mDAAmD,GACnD,OAAO,SAASC;IACd9B,cAAc+B,KAAK;AACrB"}
|
|
@@ -206,6 +206,17 @@ export interface PluginOrgCollectionDeclaration {
|
|
|
206
206
|
* declares none.
|
|
207
207
|
*/
|
|
208
208
|
siteField?: string;
|
|
209
|
+
/**
|
|
210
|
+
* The states in which a document the site owns is work still IN FLIGHT —
|
|
211
|
+
* `{ field: 'status', values: ['scheduled', 'sending'] }` — and so holds
|
|
212
|
+
* the site where it is. Moving the site to another organization is refused
|
|
213
|
+
* while any document naming it matches (AGL-3381): the work would land
|
|
214
|
+
* for an organization the site no longer belongs to. Needs `siteField`.
|
|
215
|
+
*/
|
|
216
|
+
holdsTransferWhile?: {
|
|
217
|
+
field: string;
|
|
218
|
+
values: string[];
|
|
219
|
+
};
|
|
209
220
|
}
|
|
210
221
|
/** An org declaration with the plugin that made it. */
|
|
211
222
|
export type ResolvedPluginOrgCollection = PluginOrgCollectionDeclaration & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-host-collections.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { getRegisteringPluginId } from '../app-utils/registering-plugin'\nimport { hostContentCollectionLabel } from '../foundation/definitions/host-content-collections'\nimport {\n PLUGIN_HOST_COLLECTIONS_DECLARED,\n PLUGIN_ORG_COLLECTIONS_DECLARED,\n} from './first-party-plugins.generated'\nimport {\n definePluginServiceContract,\n registerPluginService,\n resolvePluginServices,\n} from './plugin-services'\n\n/**\n * The host subcollections a plugin owns, declared by that plugin (AGL-3124).\n *\n * A first-party plugin writes ordinary documents under `hosts/{hostId}` with\n * the ordinary SDKs, and several core surfaces have to know which plugin owns\n * which of them: the media-usage scan decides what to read before an author\n * deletes an asset, a reference row deep-links to the console page that shows\n * one, and the artifact counters total what a site holds.\n *\n * Core answered all three by listing the collections and switching over plugin\n * ids — `services` and `resources` are bookings', `campaigns` and `experiments`\n * are marketing's, `functions`, `variables` and `actions` are logic's — which\n * is the platform holding a map of its plugins' storage. Every entry is also a\n * thing that can silently fall out of step: a plugin that ships a new\n * collection is invisible to all three until somebody remembers the list.\n *\n * So the plugin declares what it owns, once, and the three readers ask.\n *\n * ## What a declaration says, and what it deliberately does not\n *\n * A declaration names the collection, how the media scan should treat it, the\n * console slug a reference row links to, and whether a document in it is an\n * ARTIFACT the site's counters total beside screens and layouts. It does NOT\n * name fields: the scan flattens a document generically on purpose, and a\n * field list here would be the same staleness trap one level down.\n *\n * The media default is inherited from the scan's own rule and is the whole\n * reason that rule survives this move: **a collection is scanned unless its\n * declaration says why it is not.** A plugin that ships a media-bearing\n * collection is covered the day it lands rather than the day somebody\n * remembers it, and `mediaScan: 'none'` without a reason is refused — \"it\n * probably has no images in it\" is the guess the scan's design exists to\n * avoid making.\n *\n * ## One collection, one owner\n *\n * Two plugins writing one collection under one site would be two schemas in\n * one place, and the scan, the deep link and the counters would each pick a\n * winner by registration order. So a collection another plugin declared is\n * refused naming both, and the incumbent keeps it; the same plugin declaring\n * again replaces its own.\n *\n * ## Declared in the config, not registered at boot (AGL-3080)\n *\n * A first-party plugin declares its collections in `plugins.config.json` and\n * the generator compiles them, so every reader has the whole list the moment\n * it imports this module. That is not a preference: the media scan answers\n * \"what uses this asset\" in the moment before an author deletes it, every way\n * it can be wrong points at \"unused\", and it runs inside a console request\n * that loads no plugin code. A registry that request had not filled would\n * report every plugin-owned document as holding nothing, with nothing red —\n * the shape of AGL-3025, on the one answer here that somebody acts on.\n *\n * {@link registerPluginHostCollections} is still the door for a plugin the\n * compiler never sees, and the two compose: the compiled rows are a floor a\n * registration cannot lower, and a registration naming a compiled collection\n * is refused the same way a second plugin's would be.\n *\n * Reached by its own subpath rather than through `plugin-manager/index.ts`:\n * the readers are the media scan, the console counters and the reference\n * rows, none of which a published page renders.\n */\n\n/** How the media-usage scan reads a collection. */\nexport type PluginHostCollectionMediaScan =\n /** Flattened and searched generically, with no per-collection decoder. */\n | 'generic'\n /** Read by a pass that knows its shape, the way screens and layouts are. */\n | 'own'\n /** Not read; {@link PluginHostCollectionDeclaration.mediaScanReason} says why. */\n | 'none'\n\nexport interface PluginHostCollectionDeclaration {\n /** The subcollection under `hosts/{hostId}`, spelled as Firestore holds it. */\n name: string\n /**\n * What one of its documents is called in a reference row. Derived from the\n * collection name when absent, so a new collection reads correctly without\n * a second list to keep in step.\n */\n label?: string\n /** Default `generic` — see this module's note on why the default is to scan. */\n mediaScan?: PluginHostCollectionMediaScan\n /**\n * Why the scan does not read it. Required with `mediaScan: 'none'`, and it\n * has to answer what scanning would COST or what it would get WRONG.\n */\n mediaScanReason?: string\n /**\n * The `[pluginSlug]` segment of the plugin's console hub, for a reference\n * row's deep link. Optional on purpose: a row with no destination still\n * renders as text, which is better than the row not existing.\n */\n routeSlug?: string\n /**\n * Whether a document here is a live ARTIFACT — something the site's own\n * counters total beside its screens, layouts, components and templates.\n */\n artifact?: boolean\n}\n\n/** A declaration with the plugin that made it. */\nexport type ResolvedPluginHostCollection = PluginHostCollectionDeclaration & {\n pluginId: string\n}\n\nexport const PLUGIN_HOST_COLLECTIONS =\n definePluginServiceContract<PluginHostCollectionDeclaration>(\n 'core.host-collections',\n { multiple: true },\n )\n\n/**\n * Declares the host subcollections the registering plugin owns. The owner is\n * the loader's marker when a register fn is running, else `options.pluginId`;\n * with neither the registration throws. A collection another plugin declared\n * throws naming both, and nothing in the same call is registered — a\n * declaration is one plugin's statement about its own storage, so half of it\n * landing would be worse than none.\n */\nexport function registerPluginHostCollections(\n collections: readonly PluginHostCollectionDeclaration[],\n options?: { pluginId?: string },\n): void {\n const pluginId = (getRegisteringPluginId() ?? options?.pluginId ?? '').trim()\n const prepared = collections.map((declaration) => {\n const name = declaration.name?.trim() ?? ''\n if (!name) throw new Error('a host collection needs a name')\n if (declaration.mediaScan === 'none' && !declaration.mediaScanReason?.trim()) {\n throw new Error(\n `host collection \"${name}\" is not scanned for media and says no ` +\n 'reason: mediaScanReason has to name what scanning would cost, or ' +\n 'what it would get wrong',\n )\n }\n const incumbent = pluginHostCollection(name)\n if (incumbent && pluginId && incumbent.pluginId !== pluginId) {\n throw new Error(\n `host collection \"${name}\" is already declared by ` +\n `\"${incumbent.pluginId}\"; refused \"${pluginId}\"`,\n )\n }\n return { ...declaration, name }\n })\n for (const declaration of prepared) {\n registerPluginService(PLUGIN_HOST_COLLECTIONS, declaration, {\n ...(options?.pluginId ? { pluginId: options.pluginId } : {}),\n key: declaration.name,\n })\n }\n}\n\n/**\n * Every declared collection, with its owner: the compiled rows first, then\n * anything registered at runtime that they do not already name.\n */\nexport function listPluginHostCollections(): ResolvedPluginHostCollection[] {\n const compiled = PLUGIN_HOST_COLLECTIONS_DECLARED.map((row) => ({ ...row }))\n const named = new Set(compiled.map((row) => row.name))\n const registered = resolvePluginServices(PLUGIN_HOST_COLLECTIONS)\n .map((entry) => ({ ...entry.impl, pluginId: entry.pluginId }))\n .filter((row) => !named.has(row.name))\n return [...compiled, ...registered]\n}\n\n/** One declared collection by name, with its owner, or `null`. */\nexport function pluginHostCollection(\n name: string,\n): ResolvedPluginHostCollection | null {\n const key = name.trim()\n return listPluginHostCollections().find((one) => one.name === key) ?? null\n}\n\n/** The plugin that owns a collection, or `undefined` when none declares it. */\nexport function pluginIdForHostCollection(name: string): string | undefined {\n return pluginHostCollection(name)?.pluginId\n}\n\n/**\n * The console hub slug a reference row for this collection links to, or\n * `undefined` when the owner routes none. A row found by the org pass names an\n * org collection, so an org declaration answers when no host one does.\n */\nexport function pluginHostCollectionRouteSlug(\n name: string,\n): string | undefined {\n return pluginHostCollection(name)?.routeSlug ?? pluginOrgCollection(name)?.routeSlug\n}\n\n/**\n * The declared collections the media scan reads generically — the list the\n * scan walks, in place of one core keeps by hand.\n */\nexport function pluginHostCollectionsScannedGenerically(): string[] {\n return listPluginHostCollections()\n .filter((one) => (one.mediaScan ?? 'generic') === 'generic')\n .map((one) => one.name)\n}\n\n/** The declared collections the scan does not read, with the reason given. */\nexport function pluginHostCollectionsExcludedFromMediaScan(): Array<{\n name: string\n reason: string\n pluginId: string\n}> {\n return listPluginHostCollections()\n .filter((one) => one.mediaScan === 'none')\n .map((one) => ({\n name: one.name,\n reason: one.mediaScanReason ?? '',\n pluginId: one.pluginId,\n }))\n}\n\n/** The declared collections whose documents the site's artifact counters total. */\nexport function pluginHostArtifactCollections(): string[] {\n return listPluginHostCollections()\n .filter((one) => one.artifact === true)\n .map((one) => one.name)\n}\n\n/**\n * What to call one of a collection's documents in a reference row: the\n * declared label, else core's own derivation from the name.\n *\n * The derivation is `hostContentCollectionLabel` rather than a second copy of\n * it, because a reference row does not know or care whether the collection it\n * names belongs to a plugin — two spellings of \"Product category\" reachable\n * by two paths is the kind of difference nobody notices until a screenshot\n * disagrees with a test.\n */\nexport function pluginHostCollectionLabel(name: string): string {\n const declared = (\n pluginHostCollection(name)?.label ?? pluginOrgCollection(name)?.label\n )?.trim()\n return declared || hostContentCollectionLabel(name)\n}\n\n/**\n * The ORG collections a plugin owns, for the media-usage scan (AGL-3273).\n *\n * Everything above is about `hosts/{hostId}/…`, and the scan's completeness\n * guard sweeps the source for exactly that shape. Some plugin storage lives on\n * the organization instead — Marketing's email sends are\n * `orgs/{orgId}/campaigns`, each naming the site it was sent as — and a\n * collection the scan does not know about is read as holding nothing, which\n * is the answer \"Find where this is used\" must never give by omission.\n *\n * So a plugin declares those too, in the `orgCollections` block of\n * `plugins.config.json`, compiled like the host rows. The same inverted\n * default holds — scanned unless the declaration says why not — and\n * `siteField` names the field that says which site a document belongs to, so\n * a reference row can name that site and link into its hub.\n *\n * Compiled only. There is no runtime door: nothing outside the first-party\n * config writes org storage the scan must read, and a door nobody uses is a\n * registry the scan's request would have to be trusted to have filled.\n */\nexport interface PluginOrgCollectionDeclaration {\n /** The subcollection under `orgs/{orgId}`, spelled as Firestore holds it. */\n name: string\n /** What one of its documents is called in a reference row. */\n label?: string\n /** Default `generic`. An org collection has no dedicated pass to be `own`. */\n mediaScan?: Exclude<PluginHostCollectionMediaScan, 'own'>\n /** Why the scan does not read it. Required with `mediaScan: 'none'`. */\n mediaScanReason?: string\n /** The `[pluginSlug]` of the site hub a reference row links to. */\n routeSlug?: string\n /**\n * The field on each document naming the site it BELONGS to. Two readers\n * act on it: a reference row names that site and links into its hub, and\n * deleting the site deletes the documents that name it (`eraseHost`). A\n * collection whose documents are placed on sites rather than owned by one\n * declares none.\n */\n siteField?: string\n}\n\n/** An org declaration with the plugin that made it. */\nexport type ResolvedPluginOrgCollection = PluginOrgCollectionDeclaration & {\n pluginId: string\n}\n\n/** Every declared org collection, with its owner. */\nexport function listPluginOrgCollections(): ResolvedPluginOrgCollection[] {\n return PLUGIN_ORG_COLLECTIONS_DECLARED.map((row) => ({ ...row }))\n}\n\n/** One declared org collection by name, with its owner, or `null`. */\nexport function pluginOrgCollection(\n name: string,\n): ResolvedPluginOrgCollection | null {\n const key = name.trim()\n return listPluginOrgCollections().find((one) => one.name === key) ?? null\n}\n\n/** The declared org collections the media scan reads generically. */\nexport function pluginOrgCollectionsScannedGenerically(): ResolvedPluginOrgCollection[] {\n return listPluginOrgCollections().filter(\n (one) => (one.mediaScan ?? 'generic') === 'generic',\n )\n}\n"],"names":["getRegisteringPluginId","hostContentCollectionLabel","PLUGIN_HOST_COLLECTIONS_DECLARED","PLUGIN_ORG_COLLECTIONS_DECLARED","definePluginServiceContract","registerPluginService","resolvePluginServices","PLUGIN_HOST_COLLECTIONS","multiple","registerPluginHostCollections","collections","options","pluginId","trim","prepared","map","declaration","name","Error","mediaScan","mediaScanReason","incumbent","pluginHostCollection","key","listPluginHostCollections","compiled","row","named","Set","registered","entry","impl","filter","has","find","one","pluginIdForHostCollection","pluginHostCollectionRouteSlug","pluginOrgCollection","routeSlug","pluginHostCollectionsScannedGenerically","pluginHostCollectionsExcludedFromMediaScan","reason","pluginHostArtifactCollections","artifact","pluginHostCollectionLabel","declared","label","listPluginOrgCollections","pluginOrgCollectionsScannedGenerically"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-host-collections.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { getRegisteringPluginId } from '../app-utils/registering-plugin'\nimport { hostContentCollectionLabel } from '../foundation/definitions/host-content-collections'\nimport {\n PLUGIN_HOST_COLLECTIONS_DECLARED,\n PLUGIN_ORG_COLLECTIONS_DECLARED,\n} from './first-party-plugins.generated'\nimport {\n definePluginServiceContract,\n registerPluginService,\n resolvePluginServices,\n} from './plugin-services'\n\n/**\n * The host subcollections a plugin owns, declared by that plugin (AGL-3124).\n *\n * A first-party plugin writes ordinary documents under `hosts/{hostId}` with\n * the ordinary SDKs, and several core surfaces have to know which plugin owns\n * which of them: the media-usage scan decides what to read before an author\n * deletes an asset, a reference row deep-links to the console page that shows\n * one, and the artifact counters total what a site holds.\n *\n * Core answered all three by listing the collections and switching over plugin\n * ids — `services` and `resources` are bookings', `campaigns` and `experiments`\n * are marketing's, `functions`, `variables` and `actions` are logic's — which\n * is the platform holding a map of its plugins' storage. Every entry is also a\n * thing that can silently fall out of step: a plugin that ships a new\n * collection is invisible to all three until somebody remembers the list.\n *\n * So the plugin declares what it owns, once, and the three readers ask.\n *\n * ## What a declaration says, and what it deliberately does not\n *\n * A declaration names the collection, how the media scan should treat it, the\n * console slug a reference row links to, and whether a document in it is an\n * ARTIFACT the site's counters total beside screens and layouts. It does NOT\n * name fields: the scan flattens a document generically on purpose, and a\n * field list here would be the same staleness trap one level down.\n *\n * The media default is inherited from the scan's own rule and is the whole\n * reason that rule survives this move: **a collection is scanned unless its\n * declaration says why it is not.** A plugin that ships a media-bearing\n * collection is covered the day it lands rather than the day somebody\n * remembers it, and `mediaScan: 'none'` without a reason is refused — \"it\n * probably has no images in it\" is the guess the scan's design exists to\n * avoid making.\n *\n * ## One collection, one owner\n *\n * Two plugins writing one collection under one site would be two schemas in\n * one place, and the scan, the deep link and the counters would each pick a\n * winner by registration order. So a collection another plugin declared is\n * refused naming both, and the incumbent keeps it; the same plugin declaring\n * again replaces its own.\n *\n * ## Declared in the config, not registered at boot (AGL-3080)\n *\n * A first-party plugin declares its collections in `plugins.config.json` and\n * the generator compiles them, so every reader has the whole list the moment\n * it imports this module. That is not a preference: the media scan answers\n * \"what uses this asset\" in the moment before an author deletes it, every way\n * it can be wrong points at \"unused\", and it runs inside a console request\n * that loads no plugin code. A registry that request had not filled would\n * report every plugin-owned document as holding nothing, with nothing red —\n * the shape of AGL-3025, on the one answer here that somebody acts on.\n *\n * {@link registerPluginHostCollections} is still the door for a plugin the\n * compiler never sees, and the two compose: the compiled rows are a floor a\n * registration cannot lower, and a registration naming a compiled collection\n * is refused the same way a second plugin's would be.\n *\n * Reached by its own subpath rather than through `plugin-manager/index.ts`:\n * the readers are the media scan, the console counters and the reference\n * rows, none of which a published page renders.\n */\n\n/** How the media-usage scan reads a collection. */\nexport type PluginHostCollectionMediaScan =\n /** Flattened and searched generically, with no per-collection decoder. */\n | 'generic'\n /** Read by a pass that knows its shape, the way screens and layouts are. */\n | 'own'\n /** Not read; {@link PluginHostCollectionDeclaration.mediaScanReason} says why. */\n | 'none'\n\nexport interface PluginHostCollectionDeclaration {\n /** The subcollection under `hosts/{hostId}`, spelled as Firestore holds it. */\n name: string\n /**\n * What one of its documents is called in a reference row. Derived from the\n * collection name when absent, so a new collection reads correctly without\n * a second list to keep in step.\n */\n label?: string\n /** Default `generic` — see this module's note on why the default is to scan. */\n mediaScan?: PluginHostCollectionMediaScan\n /**\n * Why the scan does not read it. Required with `mediaScan: 'none'`, and it\n * has to answer what scanning would COST or what it would get WRONG.\n */\n mediaScanReason?: string\n /**\n * The `[pluginSlug]` segment of the plugin's console hub, for a reference\n * row's deep link. Optional on purpose: a row with no destination still\n * renders as text, which is better than the row not existing.\n */\n routeSlug?: string\n /**\n * Whether a document here is a live ARTIFACT — something the site's own\n * counters total beside its screens, layouts, components and templates.\n */\n artifact?: boolean\n}\n\n/** A declaration with the plugin that made it. */\nexport type ResolvedPluginHostCollection = PluginHostCollectionDeclaration & {\n pluginId: string\n}\n\nexport const PLUGIN_HOST_COLLECTIONS =\n definePluginServiceContract<PluginHostCollectionDeclaration>(\n 'core.host-collections',\n { multiple: true },\n )\n\n/**\n * Declares the host subcollections the registering plugin owns. The owner is\n * the loader's marker when a register fn is running, else `options.pluginId`;\n * with neither the registration throws. A collection another plugin declared\n * throws naming both, and nothing in the same call is registered — a\n * declaration is one plugin's statement about its own storage, so half of it\n * landing would be worse than none.\n */\nexport function registerPluginHostCollections(\n collections: readonly PluginHostCollectionDeclaration[],\n options?: { pluginId?: string },\n): void {\n const pluginId = (getRegisteringPluginId() ?? options?.pluginId ?? '').trim()\n const prepared = collections.map((declaration) => {\n const name = declaration.name?.trim() ?? ''\n if (!name) throw new Error('a host collection needs a name')\n if (declaration.mediaScan === 'none' && !declaration.mediaScanReason?.trim()) {\n throw new Error(\n `host collection \"${name}\" is not scanned for media and says no ` +\n 'reason: mediaScanReason has to name what scanning would cost, or ' +\n 'what it would get wrong',\n )\n }\n const incumbent = pluginHostCollection(name)\n if (incumbent && pluginId && incumbent.pluginId !== pluginId) {\n throw new Error(\n `host collection \"${name}\" is already declared by ` +\n `\"${incumbent.pluginId}\"; refused \"${pluginId}\"`,\n )\n }\n return { ...declaration, name }\n })\n for (const declaration of prepared) {\n registerPluginService(PLUGIN_HOST_COLLECTIONS, declaration, {\n ...(options?.pluginId ? { pluginId: options.pluginId } : {}),\n key: declaration.name,\n })\n }\n}\n\n/**\n * Every declared collection, with its owner: the compiled rows first, then\n * anything registered at runtime that they do not already name.\n */\nexport function listPluginHostCollections(): ResolvedPluginHostCollection[] {\n const compiled = PLUGIN_HOST_COLLECTIONS_DECLARED.map((row) => ({ ...row }))\n const named = new Set(compiled.map((row) => row.name))\n const registered = resolvePluginServices(PLUGIN_HOST_COLLECTIONS)\n .map((entry) => ({ ...entry.impl, pluginId: entry.pluginId }))\n .filter((row) => !named.has(row.name))\n return [...compiled, ...registered]\n}\n\n/** One declared collection by name, with its owner, or `null`. */\nexport function pluginHostCollection(\n name: string,\n): ResolvedPluginHostCollection | null {\n const key = name.trim()\n return listPluginHostCollections().find((one) => one.name === key) ?? null\n}\n\n/** The plugin that owns a collection, or `undefined` when none declares it. */\nexport function pluginIdForHostCollection(name: string): string | undefined {\n return pluginHostCollection(name)?.pluginId\n}\n\n/**\n * The console hub slug a reference row for this collection links to, or\n * `undefined` when the owner routes none. A row found by the org pass names an\n * org collection, so an org declaration answers when no host one does.\n */\nexport function pluginHostCollectionRouteSlug(\n name: string,\n): string | undefined {\n return pluginHostCollection(name)?.routeSlug ?? pluginOrgCollection(name)?.routeSlug\n}\n\n/**\n * The declared collections the media scan reads generically — the list the\n * scan walks, in place of one core keeps by hand.\n */\nexport function pluginHostCollectionsScannedGenerically(): string[] {\n return listPluginHostCollections()\n .filter((one) => (one.mediaScan ?? 'generic') === 'generic')\n .map((one) => one.name)\n}\n\n/** The declared collections the scan does not read, with the reason given. */\nexport function pluginHostCollectionsExcludedFromMediaScan(): Array<{\n name: string\n reason: string\n pluginId: string\n}> {\n return listPluginHostCollections()\n .filter((one) => one.mediaScan === 'none')\n .map((one) => ({\n name: one.name,\n reason: one.mediaScanReason ?? '',\n pluginId: one.pluginId,\n }))\n}\n\n/** The declared collections whose documents the site's artifact counters total. */\nexport function pluginHostArtifactCollections(): string[] {\n return listPluginHostCollections()\n .filter((one) => one.artifact === true)\n .map((one) => one.name)\n}\n\n/**\n * What to call one of a collection's documents in a reference row: the\n * declared label, else core's own derivation from the name.\n *\n * The derivation is `hostContentCollectionLabel` rather than a second copy of\n * it, because a reference row does not know or care whether the collection it\n * names belongs to a plugin — two spellings of \"Product category\" reachable\n * by two paths is the kind of difference nobody notices until a screenshot\n * disagrees with a test.\n */\nexport function pluginHostCollectionLabel(name: string): string {\n const declared = (\n pluginHostCollection(name)?.label ?? pluginOrgCollection(name)?.label\n )?.trim()\n return declared || hostContentCollectionLabel(name)\n}\n\n/**\n * The ORG collections a plugin owns, for the media-usage scan (AGL-3273).\n *\n * Everything above is about `hosts/{hostId}/…`, and the scan's completeness\n * guard sweeps the source for exactly that shape. Some plugin storage lives on\n * the organization instead — Marketing's email sends are\n * `orgs/{orgId}/campaigns`, each naming the site it was sent as — and a\n * collection the scan does not know about is read as holding nothing, which\n * is the answer \"Find where this is used\" must never give by omission.\n *\n * So a plugin declares those too, in the `orgCollections` block of\n * `plugins.config.json`, compiled like the host rows. The same inverted\n * default holds — scanned unless the declaration says why not — and\n * `siteField` names the field that says which site a document belongs to, so\n * a reference row can name that site and link into its hub.\n *\n * Compiled only. There is no runtime door: nothing outside the first-party\n * config writes org storage the scan must read, and a door nobody uses is a\n * registry the scan's request would have to be trusted to have filled.\n */\nexport interface PluginOrgCollectionDeclaration {\n /** The subcollection under `orgs/{orgId}`, spelled as Firestore holds it. */\n name: string\n /** What one of its documents is called in a reference row. */\n label?: string\n /** Default `generic`. An org collection has no dedicated pass to be `own`. */\n mediaScan?: Exclude<PluginHostCollectionMediaScan, 'own'>\n /** Why the scan does not read it. Required with `mediaScan: 'none'`. */\n mediaScanReason?: string\n /** The `[pluginSlug]` of the site hub a reference row links to. */\n routeSlug?: string\n /**\n * The field on each document naming the site it BELONGS to. Two readers\n * act on it: a reference row names that site and links into its hub, and\n * deleting the site deletes the documents that name it (`eraseHost`). A\n * collection whose documents are placed on sites rather than owned by one\n * declares none.\n */\n siteField?: string\n /**\n * The states in which a document the site owns is work still IN FLIGHT —\n * `{ field: 'status', values: ['scheduled', 'sending'] }` — and so holds\n * the site where it is. Moving the site to another organization is refused\n * while any document naming it matches (AGL-3381): the work would land\n * for an organization the site no longer belongs to. Needs `siteField`.\n */\n holdsTransferWhile?: { field: string; values: string[] }\n}\n\n/** An org declaration with the plugin that made it. */\nexport type ResolvedPluginOrgCollection = PluginOrgCollectionDeclaration & {\n pluginId: string\n}\n\n/** Every declared org collection, with its owner. */\nexport function listPluginOrgCollections(): ResolvedPluginOrgCollection[] {\n return PLUGIN_ORG_COLLECTIONS_DECLARED.map((row) => ({ ...row }))\n}\n\n/** One declared org collection by name, with its owner, or `null`. */\nexport function pluginOrgCollection(\n name: string,\n): ResolvedPluginOrgCollection | null {\n const key = name.trim()\n return listPluginOrgCollections().find((one) => one.name === key) ?? null\n}\n\n/** The declared org collections the media scan reads generically. */\nexport function pluginOrgCollectionsScannedGenerically(): ResolvedPluginOrgCollection[] {\n return listPluginOrgCollections().filter(\n (one) => (one.mediaScan ?? 'generic') === 'generic',\n )\n}\n"],"names":["getRegisteringPluginId","hostContentCollectionLabel","PLUGIN_HOST_COLLECTIONS_DECLARED","PLUGIN_ORG_COLLECTIONS_DECLARED","definePluginServiceContract","registerPluginService","resolvePluginServices","PLUGIN_HOST_COLLECTIONS","multiple","registerPluginHostCollections","collections","options","pluginId","trim","prepared","map","declaration","name","Error","mediaScan","mediaScanReason","incumbent","pluginHostCollection","key","listPluginHostCollections","compiled","row","named","Set","registered","entry","impl","filter","has","find","one","pluginIdForHostCollection","pluginHostCollectionRouteSlug","pluginOrgCollection","routeSlug","pluginHostCollectionsScannedGenerically","pluginHostCollectionsExcludedFromMediaScan","reason","pluginHostArtifactCollections","artifact","pluginHostCollectionLabel","declared","label","listPluginOrgCollections","pluginOrgCollectionsScannedGenerically"],"mappings":"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"}
|