@aglyn/aglyn 1.0.0-beta.268 → 1.0.0-beta.270
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/account-preferences.d.ts +248 -0
- package/src/lib/app-utils/account-preferences.js +434 -0
- package/src/lib/app-utils/account-preferences.js.map +1 -0
- package/src/lib/app-utils/country-options.d.ts +27 -0
- package/src/lib/app-utils/country-options.js +96 -0
- package/src/lib/app-utils/country-options.js.map +1 -0
- package/src/lib/app-utils/docs-help.generated.d.ts +6 -0
- package/src/lib/app-utils/docs-help.generated.js +13 -0
- package/src/lib/app-utils/docs-help.generated.js.map +1 -1
- package/src/lib/app-utils/docs-index.generated.js +17 -10
- package/src/lib/app-utils/docs-index.generated.js.map +1 -1
- package/src/lib/app-utils/element-motion.d.ts +14 -2
- package/src/lib/app-utils/element-motion.js +33 -13
- package/src/lib/app-utils/element-motion.js.map +1 -1
- package/src/lib/app-utils/mobile-push.d.ts +8 -0
- package/src/lib/app-utils/mobile-push.js +4 -1
- package/src/lib/app-utils/mobile-push.js.map +1 -1
- package/src/lib/app-utils/native-app-association.d.ts +3 -2
- package/src/lib/app-utils/native-app-association.js +4 -2
- package/src/lib/app-utils/native-app-association.js.map +1 -1
- package/src/lib/app-utils/native-nav.d.ts +265 -0
- package/src/lib/app-utils/native-nav.js +741 -0
- package/src/lib/app-utils/native-nav.js.map +1 -0
- package/src/lib/app-utils/native-themes.d.ts +249 -0
- package/src/lib/app-utils/native-themes.js +732 -0
- package/src/lib/app-utils/native-themes.js.map +1 -0
- package/src/lib/app-utils/notifications.d.ts +1 -1
- package/src/lib/app-utils/notifications.js +10 -0
- package/src/lib/app-utils/notifications.js.map +1 -1
- package/src/lib/app-utils/one-box-search.d.ts +108 -0
- package/src/lib/app-utils/one-box-search.js +167 -0
- package/src/lib/app-utils/one-box-search.js.map +1 -0
- package/src/lib/app-utils/org-permissions.js +10 -61
- package/src/lib/app-utils/org-permissions.js.map +1 -1
- package/src/lib/app-utils/org-role-grants.d.ts +95 -0
- package/src/lib/app-utils/org-role-grants.js +193 -0
- package/src/lib/app-utils/org-role-grants.js.map +1 -0
- package/src/lib/app-utils/plan-entitlements.d.ts +41 -1
- package/src/lib/app-utils/plan-entitlements.js +65 -3
- package/src/lib/app-utils/plan-entitlements.js.map +1 -1
- package/src/lib/app-utils/tenant-dns-targets.d.ts +62 -0
- package/src/lib/app-utils/tenant-dns-targets.js +80 -0
- package/src/lib/app-utils/tenant-dns-targets.js.map +1 -0
- package/src/lib/foundation/definitions/org-billing.types.d.ts +14 -0
- package/src/lib/foundation/definitions/org-billing.types.js.map +1 -1
- package/src/lib/foundation/definitions/platform.types.d.ts +9 -0
- package/src/lib/foundation/definitions/platform.types.js.map +1 -1
- package/src/lib/plugin-manager/feature-plugins.d.ts +38 -0
- package/src/lib/plugin-manager/feature-plugins.js +20 -0
- package/src/lib/plugin-manager/feature-plugins.js.map +1 -1
- package/src/lib/plugin-manager/first-party-plugins.generated.js +22 -1
- package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
- package/src/lib/plugin-manager/operator-alerts.js +10 -1
- package/src/lib/plugin-manager/operator-alerts.js.map +1 -1
- package/src/lib/plugin-manager/plugin-config.js +10 -1
- package/src/lib/plugin-manager/plugin-config.js.map +1 -1
- package/src/lib/plugin-manager/plugin-consent-group-change.js +11 -2
- package/src/lib/plugin-manager/plugin-consent-group-change.js.map +1 -1
- package/src/lib/plugin-manager/plugin-console-crons.js +10 -1
- package/src/lib/plugin-manager/plugin-console-crons.js.map +1 -1
- package/src/lib/plugin-manager/plugin-domain-events.d.ts +4 -3
- package/src/lib/plugin-manager/plugin-domain-events.js.map +1 -1
- package/src/lib/plugin-manager/plugin-events.js +11 -2
- package/src/lib/plugin-manager/plugin-events.js.map +1 -1
- package/src/lib/plugin-manager/plugin-lead-conversion.js +10 -1
- package/src/lib/plugin-manager/plugin-lead-conversion.js.map +1 -1
- package/src/lib/plugin-manager/plugin-media-ingest.d.ts +25 -0
- package/src/lib/plugin-manager/plugin-media-ingest.js.map +1 -1
- package/src/lib/plugin-manager/plugin-membership-detach.js +10 -1
- package/src/lib/plugin-manager/plugin-membership-detach.js.map +1 -1
- package/src/lib/plugin-manager/plugin-person-matches.js +10 -1
- package/src/lib/plugin-manager/plugin-person-matches.js.map +1 -1
- package/src/lib/plugin-manager/plugin-search-sources.d.ts +85 -0
- package/src/lib/plugin-manager/plugin-search-sources.js +91 -0
- package/src/lib/plugin-manager/plugin-search-sources.js.map +1 -0
- package/src/lib/plugin-manager/plugin-services.js +6 -0
- package/src/lib/plugin-manager/plugin-services.js.map +1 -1
- package/src/lib/plugin-manager/plugin-usage-meters.js +10 -1
- package/src/lib/plugin-manager/plugin-usage-meters.js.map +1 -1
- package/src/lib/plugin-manager/usage-alert-contributors.js +10 -1
- package/src/lib/plugin-manager/usage-alert-contributors.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/usage-alert-contributors.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\n/**\n * Alerts a plugin contributes to the usage-alerts sweep (AGL-2984).\n *\n * The usage-alerts cron walks every org, reads its usage once, and sends\n * core's own alerts: the plan quotas, the customer's budget and the free\n * plan's bandwidth cap. A plugin that meters a cost or enforces a ceiling\n * core knows nothing about registers a contributor here from its\n * `serverDeclarations` entry, in either or both of two shapes:\n *\n * - `quotaChecks` answers the bands the WORKSPACE is warned about — a used\n * figure against a limit, how often it may be announced, what happens at\n * it, and the words for approaching and reaching it. The sweep runs them\n * after its own quota checks, through the same approach threshold, guards\n * and both channels, so a plugin's band warns exactly as a platform band\n * does.\n * - `evaluate` decides STAFF alerts, and the sweep hands it each org's\n * reading after the budget alert and before the bandwidth cap.\n *\n * THE PIPELINE IS THE SWEEP'S. A contributor decides whether an alert is due\n * and what it says; the context does everything else:\n *\n * - `recordAlert(key, threshold)` records the dedupe guard and answers\n * whether the alert may be SENT. On an org's first, silent evaluation it\n * records the guard as though the alert went out and answers `false`, so a\n * contributor that sends only on `true` honors the first-sweep seeding\n * without knowing when it applies.\n * - `alertStaff(alert)` is the sweep's own sender: the staff bell, then the\n * staff inbox, then a row in the run's report saying whether the mail went\n * out. On an org's first, silent evaluation it sends nothing, whoever calls\n * it.\n * - `guards` is the org's guard map as the sweep read it. A contributor reads\n * it to dedupe and never writes a guard itself: the sweep writes what\n * `recordAlert` recorded, once, with the rest of the org's update.\n *\n * Guard keys share one map with core's own checks (`hosts`, `bandwidth`,\n * `budget` and the rest), so a contributor names its keys for what it\n * measures and never reuses one of core's.\n *\n * Contributors run one at a time and are ISOLATED: a throw is logged against\n * the contributor and the next one runs. A guard the failed contributor\n * recorded for an alert it never delivered is dropped rather than written, so\n * the next sweep tries that alert again instead of counting it as announced.\n * One plugin's failure costs no other plugin's alert, none of core's, and not\n * the rest of the sweep.\n *\n * The order is deterministic: the catalog order of `FIRST_PARTY_PLUGINS`,\n * then any other plugin id in code-unit order, then registration order within\n * a plugin. The order contributors REGISTER in depends on how each process\n * loaded the declarations, and a list that followed it could send and report\n * one org's alerts in a different order per process.\n */\n\nimport type { NotificationLevel } from '../app-utils/notifications'\nimport type {\n OrgSpendBreakdown,\n UsageAlertGuard,\n} from '../app-utils/usage-budget'\nimport { FIRST_PARTY_PLUGINS } from './enabled-plugins'\n\n/** One staff alert, as the sweep delivers and reports it. */\nexport interface UsageStaffAlert {\n /** The guard key the alert was recorded under; the report row's `quota`. */\n quota: string\n /** The threshold passed to `recordAlert`; the report row's `threshold`. */\n threshold: number\n /** The bell's title and the email's subject: one set of words for both. */\n title: string\n /** The bell's body. The email carries it verbatim, followed by the link. */\n body: string\n /** A console path. The bell links it; the email makes it absolute. */\n link: string\n /** The email's tag and log label. */\n emailContext: string\n /**\n * How loud the bell draws it (AGL-3437). A contributor's threshold is its\n * own unit, not a percentage, so only the contributor can say; absent, the\n * `billing.usage` default answers.\n */\n level?: NotificationLevel\n}\n\n/** What the sweep hands a contributor for one org. */\nexport interface UsageAlertContext {\n orgId: string\n /** The org's slug, or `null` when the document carries none. */\n orgSlug: string | null\n /** The org document as the sweep read it. */\n org: Readonly<Record<string, unknown>>\n /** The `YYYY-MM` month the sweep dedupes against, fixed for the whole run. */\n month: string\n /** The org's spend this month, built from the figures the sweep read. */\n spend: Readonly<OrgSpendBreakdown>\n /** The org's guard map as the sweep read it, before this run's updates. */\n guards: Readonly<Record<string, Readonly<UsageAlertGuard>>>\n /**\n * Records the guard for `key` at `threshold` and answers whether the alert\n * may be sent. Call it once the alert is due, and send only on `true`.\n */\n recordAlert: (key: string, threshold: number) => boolean\n /** Delivers one staff alert through the sweep's senders and reports it. */\n alertStaff: (alert: UsageStaffAlert) => Promise<void>\n}\n\n/**\n * One band a workspace is warned about as it approaches and reaches it — the\n * shape of a plugin's quota check.\n *\n * The sweep owns everything past the numbers and the words: the approach\n * percentage, the guard under `key`, the first-sweep seeding, both channels,\n * and the sentence every notice opens with — the workspace, then the figures,\n * then `noun` (\"The Acme workspace has used 2,300 of the 2,750 AI assist\n * credits your plan includes this month\").\n */\nexport interface UsageQuotaCheck {\n /** The guard key; named for what it measures, never one of core's. */\n key: string\n /** How the alert's title names the band: \"AI assist credits\". */\n label: string\n /** What the figures count, in the opening sentence. */\n noun: string\n used: number\n /** `UNLIMITED` or anything not above 0 is skipped. */\n limit: number\n /**\n * How often one threshold may be announced: `crossing` for something the\n * workspace HAS, announced once when reached and again only after usage\n * falls back below; `monthly` for a meter that resets on the 1st.\n */\n cadence: 'crossing' | 'monthly'\n /**\n * What happens at the band, which the notice has to say because the three\n * call for different actions: `stops` (new use is refused, nothing is\n * charged), `bills` (it keeps working and starts charging) or `continues`\n * (nothing is refused and nothing is charged).\n */\n outcome: 'stops' | 'bills' | 'continues'\n /** What is true now, at or past the band. */\n reached: string\n /** What will happen at the band, said while approaching it. */\n approach: string\n /** Replaces the generic title at 100%, when that would not be true. */\n reachedTitle?: string\n /**\n * Replaces the figures in the opening sentence at 100%, after the\n * workspace's name (\"has used its free AI credits for this month.\"). For a\n * band nothing is ever sold past, where \"442 — more than the 300 your plan\n * includes\" reads like a charge that cannot happen.\n */\n reachedLead?: string\n /**\n * At 100%, link the plans page instead of the usage section: the band is\n * an allowance that only an upgrade adds to, and the notice says so.\n */\n reachedLinksUpgrade?: boolean\n}\n\n/** What the sweep hands a contributor's quota checks for one org. */\nexport interface UsageQuotaContext {\n orgId: string\n /** The org document as the sweep read it. */\n org: Readonly<Record<string, unknown>>\n /** The `YYYY-MM` month the sweep dedupes against. */\n month: string\n /** The org's spend this month, built from the figures the sweep read. */\n spend: Readonly<OrgSpendBreakdown>\n}\n\n/** A plugin's rule for the usage-alerts sweep. */\nexport interface UsageAlertContributor {\n pluginId: string\n /** Unique within the plugin; two plugins may use the same id. */\n id: string\n /** Evaluates one org's STAFF alerts, sending only through the context. */\n evaluate?: (context: UsageAlertContext) => Promise<void>\n /**\n * The bands the WORKSPACE is warned about, which the sweep runs after its\n * own through the same thresholds, guards and senders.\n */\n quotaChecks?: (context: UsageQuotaContext) => Promise<readonly UsageQuotaCheck[]>\n}\n\nconst registrations: UsageAlertContributor[] = []\n\nconst CATALOG_ORDER = new Map(\n FIRST_PARTY_PLUGINS.map((plugin, index) => [plugin.id, index]),\n)\n\nfunction catalogRank(pluginId: string): number {\n return CATALOG_ORDER.get(pluginId) ?? Number.MAX_SAFE_INTEGER\n}\n\n/** Code-unit order, which no locale can reorder. */\nfunction codeUnitOrder(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0\n}\n\n/**\n * Registers a contributor. Idempotent per plugin and id: registering the same\n * pair again replaces the earlier contributor in place, so a declarations\n * module evaluated twice does not alert twice. A contributor with no plugin,\n * no id, or neither `evaluate` nor `quotaChecks` throws.\n */\nexport function registerUsageAlertContributor(\n contributor: UsageAlertContributor,\n): void {\n const pluginId = String(contributor.pluginId ?? '').trim()\n const id = String(contributor.id ?? '').trim()\n if (!pluginId || !id) {\n throw new Error('a usage alert contributor needs a pluginId and an id')\n }\n const evaluate =\n typeof contributor.evaluate === 'function' ? contributor.evaluate : undefined\n const quotaChecks =\n typeof contributor.quotaChecks === 'function'\n ? contributor.quotaChecks\n : undefined\n if (!evaluate && !quotaChecks) {\n throw new Error(\n `usage alert contributor \"${pluginId}:${id}\" has neither an evaluate ` +\n 'nor a quotaChecks function',\n )\n }\n const entry: UsageAlertContributor = {\n pluginId,\n id,\n ...(evaluate ? { evaluate } : {}),\n ...(quotaChecks ? { quotaChecks } : {}),\n }\n const index = registrations.findIndex(\n (existing) => existing.pluginId === pluginId && existing.id === id,\n )\n if (index >= 0) registrations[index] = entry\n else registrations.push(entry)\n}\n\n/** Every registered contributor, in the order the sweep runs them. */\nexport function listUsageAlertContributors(): UsageAlertContributor[] {\n return registrations\n .map((entry, sequence) => ({ entry, sequence }))\n .sort(\n (a, b) =>\n catalogRank(a.entry.pluginId) - catalogRank(b.entry.pluginId) ||\n codeUnitOrder(a.entry.pluginId, b.entry.pluginId) ||\n a.sequence - b.sequence,\n )\n .map(({ entry }) => ({ ...entry }))\n}\n\n/** Test seam: forget every contributor. */\nexport function resetUsageAlertContributorsForTests(): void {\n registrations.length = 0\n}\n"],"names":["FIRST_PARTY_PLUGINS","registrations","CATALOG_ORDER","Map","map","plugin","index","id","catalogRank","pluginId","get","Number","MAX_SAFE_INTEGER","codeUnitOrder","a","b","registerUsageAlertContributor","contributor","String","trim","Error","evaluate","undefined","quotaChecks","entry","findIndex","existing","push","listUsageAlertContributors","sequence","sort","resetUsageAlertContributorsForTests","length"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkDC;AAOD,SAASA,mBAAmB,QAAQ,uBAAmB;AA4HvD,MAAMC,gBAAyC,EAAE;AAEjD,MAAMC,gBAAgB,IAAIC,IACxBH,oBAAoBI,GAAG,CAAC,CAACC,QAAQC,QAAU;QAACD,OAAOE,EAAE;QAAED;KAAM;AAG/D,SAASE,YAAYC,QAAgB;QAC5BP;IAAP,QAAOA,qBAAAA,cAAcQ,GAAG,CAACD,qBAAlBP,qBAA+BS,OAAOC,gBAAgB;AAC/D;AAEA,kDAAkD,GAClD,SAASC,cAAcC,CAAS,EAAEC,CAAS;IACzC,OAAOD,IAAIC,IAAI,CAAC,IAAID,IAAIC,IAAI,IAAI;AAClC;AAEA;;;;;CAKC,GACD,OAAO,SAASC,8BACdC,WAAkC;QAEVA,uBACNA;IADlB,MAAMR,WAAWS,QAAOD,wBAAAA,YAAYR,QAAQ,YAApBQ,wBAAwB,IAAIE,IAAI;IACxD,MAAMZ,KAAKW,QAAOD,kBAAAA,YAAYV,EAAE,YAAdU,kBAAkB,IAAIE,IAAI;IAC5C,IAAI,CAACV,YAAY,CAACF,IAAI;QACpB,MAAM,IAAIa,MAAM;IAClB;IACA,MAAMC,WACJ,OAAOJ,YAAYI,QAAQ,KAAK,aAAaJ,YAAYI,QAAQ,GAAGC;IACtE,MAAMC,cACJ,OAAON,YAAYM,WAAW,KAAK,aAC/BN,YAAYM,WAAW,GACvBD;IACN,IAAI,CAACD,YAAY,CAACE,aAAa;QAC7B,MAAM,IAAIH,MACR,CAAC,yBAAyB,EAAEX,SAAS,CAAC,EAAEF,GAAG,0BAA0B,CAAC,GACpE;IAEN;IACA,MAAMiB,QAA+B;QACnCf;QACAF;OACIc,WAAW;QAAEA;IAAS,IAAI,CAAC,GAC3BE,cAAc;QAAEA;IAAY,IAAI,CAAC;IAEvC,MAAMjB,QAAQL,cAAcwB,SAAS,CACnC,CAACC,WAAaA,SAASjB,QAAQ,KAAKA,YAAYiB,SAASnB,EAAE,KAAKA;IAElE,IAAID,SAAS,GAAGL,aAAa,CAACK,MAAM,GAAGkB;SAClCvB,cAAc0B,IAAI,CAACH;AAC1B;AAEA,oEAAoE,GACpE,OAAO,SAASI;IACd,OAAO3B,cACJG,GAAG,CAAC,CAACoB,OAAOK,WAAc,CAAA;YAAEL;YAAOK;QAAS,CAAA,GAC5CC,IAAI,CACH,CAAChB,GAAGC,IACFP,YAAYM,EAAEU,KAAK,CAACf,QAAQ,IAAID,YAAYO,EAAES,KAAK,CAACf,QAAQ,KAC5DI,cAAcC,EAAEU,KAAK,CAACf,QAAQ,EAAEM,EAAES,KAAK,CAACf,QAAQ,KAChDK,EAAEe,QAAQ,GAAGd,EAAEc,QAAQ,EAE1BzB,GAAG,CAAC,CAAC,EAAEoB,KAAK,EAAE,GAAM,aAAKA;AAC9B;AAEA,yCAAyC,GACzC,OAAO,SAASO;IACd9B,cAAc+B,MAAM,GAAG;AACzB"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/usage-alert-contributors.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\n/**\n * Alerts a plugin contributes to the usage-alerts sweep (AGL-2984).\n *\n * The usage-alerts cron walks every org, reads its usage once, and sends\n * core's own alerts: the plan quotas, the customer's budget and the free\n * plan's bandwidth cap. A plugin that meters a cost or enforces a ceiling\n * core knows nothing about registers a contributor here from its\n * `serverDeclarations` entry, in either or both of two shapes:\n *\n * - `quotaChecks` answers the bands the WORKSPACE is warned about — a used\n * figure against a limit, how often it may be announced, what happens at\n * it, and the words for approaching and reaching it. The sweep runs them\n * after its own quota checks, through the same approach threshold, guards\n * and both channels, so a plugin's band warns exactly as a platform band\n * does.\n * - `evaluate` decides STAFF alerts, and the sweep hands it each org's\n * reading after the budget alert and before the bandwidth cap.\n *\n * THE PIPELINE IS THE SWEEP'S. A contributor decides whether an alert is due\n * and what it says; the context does everything else:\n *\n * - `recordAlert(key, threshold)` records the dedupe guard and answers\n * whether the alert may be SENT. On an org's first, silent evaluation it\n * records the guard as though the alert went out and answers `false`, so a\n * contributor that sends only on `true` honors the first-sweep seeding\n * without knowing when it applies.\n * - `alertStaff(alert)` is the sweep's own sender: the staff bell, then the\n * staff inbox, then a row in the run's report saying whether the mail went\n * out. On an org's first, silent evaluation it sends nothing, whoever calls\n * it.\n * - `guards` is the org's guard map as the sweep read it. A contributor reads\n * it to dedupe and never writes a guard itself: the sweep writes what\n * `recordAlert` recorded, once, with the rest of the org's update.\n *\n * Guard keys share one map with core's own checks (`hosts`, `bandwidth`,\n * `budget` and the rest), so a contributor names its keys for what it\n * measures and never reuses one of core's.\n *\n * Contributors run one at a time and are ISOLATED: a throw is logged against\n * the contributor and the next one runs. A guard the failed contributor\n * recorded for an alert it never delivered is dropped rather than written, so\n * the next sweep tries that alert again instead of counting it as announced.\n * One plugin's failure costs no other plugin's alert, none of core's, and not\n * the rest of the sweep.\n *\n * The order is deterministic: the catalog order of `FIRST_PARTY_PLUGINS`,\n * then any other plugin id in code-unit order, then registration order within\n * a plugin. The order contributors REGISTER in depends on how each process\n * loaded the declarations, and a list that followed it could send and report\n * one org's alerts in a different order per process.\n */\n\nimport type { NotificationLevel } from '../app-utils/notifications'\nimport type {\n OrgSpendBreakdown,\n UsageAlertGuard,\n} from '../app-utils/usage-budget'\nimport { FIRST_PARTY_PLUGINS } from './enabled-plugins'\n\n/** One staff alert, as the sweep delivers and reports it. */\nexport interface UsageStaffAlert {\n /** The guard key the alert was recorded under; the report row's `quota`. */\n quota: string\n /** The threshold passed to `recordAlert`; the report row's `threshold`. */\n threshold: number\n /** The bell's title and the email's subject: one set of words for both. */\n title: string\n /** The bell's body. The email carries it verbatim, followed by the link. */\n body: string\n /** A console path. The bell links it; the email makes it absolute. */\n link: string\n /** The email's tag and log label. */\n emailContext: string\n /**\n * How loud the bell draws it (AGL-3437). A contributor's threshold is its\n * own unit, not a percentage, so only the contributor can say; absent, the\n * `billing.usage` default answers.\n */\n level?: NotificationLevel\n}\n\n/** What the sweep hands a contributor for one org. */\nexport interface UsageAlertContext {\n orgId: string\n /** The org's slug, or `null` when the document carries none. */\n orgSlug: string | null\n /** The org document as the sweep read it. */\n org: Readonly<Record<string, unknown>>\n /** The `YYYY-MM` month the sweep dedupes against, fixed for the whole run. */\n month: string\n /** The org's spend this month, built from the figures the sweep read. */\n spend: Readonly<OrgSpendBreakdown>\n /** The org's guard map as the sweep read it, before this run's updates. */\n guards: Readonly<Record<string, Readonly<UsageAlertGuard>>>\n /**\n * Records the guard for `key` at `threshold` and answers whether the alert\n * may be sent. Call it once the alert is due, and send only on `true`.\n */\n recordAlert: (key: string, threshold: number) => boolean\n /** Delivers one staff alert through the sweep's senders and reports it. */\n alertStaff: (alert: UsageStaffAlert) => Promise<void>\n}\n\n/**\n * One band a workspace is warned about as it approaches and reaches it — the\n * shape of a plugin's quota check.\n *\n * The sweep owns everything past the numbers and the words: the approach\n * percentage, the guard under `key`, the first-sweep seeding, both channels,\n * and the sentence every notice opens with — the workspace, then the figures,\n * then `noun` (\"The Acme workspace has used 2,300 of the 2,750 AI assist\n * credits your plan includes this month\").\n */\nexport interface UsageQuotaCheck {\n /** The guard key; named for what it measures, never one of core's. */\n key: string\n /** How the alert's title names the band: \"AI assist credits\". */\n label: string\n /** What the figures count, in the opening sentence. */\n noun: string\n used: number\n /** `UNLIMITED` or anything not above 0 is skipped. */\n limit: number\n /**\n * How often one threshold may be announced: `crossing` for something the\n * workspace HAS, announced once when reached and again only after usage\n * falls back below; `monthly` for a meter that resets on the 1st.\n */\n cadence: 'crossing' | 'monthly'\n /**\n * What happens at the band, which the notice has to say because the three\n * call for different actions: `stops` (new use is refused, nothing is\n * charged), `bills` (it keeps working and starts charging) or `continues`\n * (nothing is refused and nothing is charged).\n */\n outcome: 'stops' | 'bills' | 'continues'\n /** What is true now, at or past the band. */\n reached: string\n /** What will happen at the band, said while approaching it. */\n approach: string\n /** Replaces the generic title at 100%, when that would not be true. */\n reachedTitle?: string\n /**\n * Replaces the figures in the opening sentence at 100%, after the\n * workspace's name (\"has used its free AI credits for this month.\"). For a\n * band nothing is ever sold past, where \"442 — more than the 300 your plan\n * includes\" reads like a charge that cannot happen.\n */\n reachedLead?: string\n /**\n * At 100%, link the plans page instead of the usage section: the band is\n * an allowance that only an upgrade adds to, and the notice says so.\n */\n reachedLinksUpgrade?: boolean\n}\n\n/** What the sweep hands a contributor's quota checks for one org. */\nexport interface UsageQuotaContext {\n orgId: string\n /** The org document as the sweep read it. */\n org: Readonly<Record<string, unknown>>\n /** The `YYYY-MM` month the sweep dedupes against. */\n month: string\n /** The org's spend this month, built from the figures the sweep read. */\n spend: Readonly<OrgSpendBreakdown>\n}\n\n/** A plugin's rule for the usage-alerts sweep. */\nexport interface UsageAlertContributor {\n pluginId: string\n /** Unique within the plugin; two plugins may use the same id. */\n id: string\n /** Evaluates one org's STAFF alerts, sending only through the context. */\n evaluate?: (context: UsageAlertContext) => Promise<void>\n /**\n * The bands the WORKSPACE is warned about, which the sweep runs after its\n * own through the same thresholds, guards and senders.\n */\n quotaChecks?: (context: UsageQuotaContext) => Promise<readonly UsageQuotaCheck[]>\n}\n\n/**\n * On `globalThis` under `Symbol.for`, never at module scope (AGL-3947).\n * The contributors are registered at boot, from `instrumentation.ts`, and Next\n * compiles that file apart from the routes, so the boot and a route each hold\n * their own copy of this module. At module scope, the boot filled its copy and\n * every route read an empty one (AGL-3412, AGL-3868).\n */\nconst REGISTRY_KEY = Symbol.for('@aglyn/aglyn:usage-alert-contributors')\nconst globalScope = globalThis as typeof globalThis & {\n [REGISTRY_KEY]?: UsageAlertContributor[]\n}\nconst registrations: UsageAlertContributor[] =\n globalScope[REGISTRY_KEY] ?? (globalScope[REGISTRY_KEY] = [])\n\nconst CATALOG_ORDER = new Map(\n FIRST_PARTY_PLUGINS.map((plugin, index) => [plugin.id, index]),\n)\n\nfunction catalogRank(pluginId: string): number {\n return CATALOG_ORDER.get(pluginId) ?? Number.MAX_SAFE_INTEGER\n}\n\n/** Code-unit order, which no locale can reorder. */\nfunction codeUnitOrder(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0\n}\n\n/**\n * Registers a contributor. Idempotent per plugin and id: registering the same\n * pair again replaces the earlier contributor in place, so a declarations\n * module evaluated twice does not alert twice. A contributor with no plugin,\n * no id, or neither `evaluate` nor `quotaChecks` throws.\n */\nexport function registerUsageAlertContributor(\n contributor: UsageAlertContributor,\n): void {\n const pluginId = String(contributor.pluginId ?? '').trim()\n const id = String(contributor.id ?? '').trim()\n if (!pluginId || !id) {\n throw new Error('a usage alert contributor needs a pluginId and an id')\n }\n const evaluate =\n typeof contributor.evaluate === 'function' ? contributor.evaluate : undefined\n const quotaChecks =\n typeof contributor.quotaChecks === 'function'\n ? contributor.quotaChecks\n : undefined\n if (!evaluate && !quotaChecks) {\n throw new Error(\n `usage alert contributor \"${pluginId}:${id}\" has neither an evaluate ` +\n 'nor a quotaChecks function',\n )\n }\n const entry: UsageAlertContributor = {\n pluginId,\n id,\n ...(evaluate ? { evaluate } : {}),\n ...(quotaChecks ? { quotaChecks } : {}),\n }\n const index = registrations.findIndex(\n (existing) => existing.pluginId === pluginId && existing.id === id,\n )\n if (index >= 0) registrations[index] = entry\n else registrations.push(entry)\n}\n\n/** Every registered contributor, in the order the sweep runs them. */\nexport function listUsageAlertContributors(): UsageAlertContributor[] {\n return registrations\n .map((entry, sequence) => ({ entry, sequence }))\n .sort(\n (a, b) =>\n catalogRank(a.entry.pluginId) - catalogRank(b.entry.pluginId) ||\n codeUnitOrder(a.entry.pluginId, b.entry.pluginId) ||\n a.sequence - b.sequence,\n )\n .map(({ entry }) => ({ ...entry }))\n}\n\n/** Test seam: forget every contributor. */\nexport function resetUsageAlertContributorsForTests(): void {\n registrations.length = 0\n}\n"],"names":["globalScope","FIRST_PARTY_PLUGINS","REGISTRY_KEY","Symbol","for","globalThis","registrations","CATALOG_ORDER","Map","map","plugin","index","id","catalogRank","pluginId","get","Number","MAX_SAFE_INTEGER","codeUnitOrder","a","b","registerUsageAlertContributor","contributor","String","trim","Error","evaluate","undefined","quotaChecks","entry","findIndex","existing","push","listUsageAlertContributors","sequence","sort","resetUsageAlertContributorsForTests","length"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkDC;IA+ICA;AAxIF,SAASC,mBAAmB,QAAQ,uBAAmB;AA4HvD;;;;;;CAMC,GACD,MAAMC,eAAeC,OAAOC,GAAG,CAAC;AAChC,MAAMJ,cAAcK;AAGpB,MAAMC,iBACJN,4BAAAA,WAAW,CAACE,aAAa,YAAzBF,4BAA8BA,WAAW,CAACE,aAAa,GAAG,EAAE;AAE9D,MAAMK,gBAAgB,IAAIC,IACxBP,oBAAoBQ,GAAG,CAAC,CAACC,QAAQC,QAAU;QAACD,OAAOE,EAAE;QAAED;KAAM;AAG/D,SAASE,YAAYC,QAAgB;QAC5BP;IAAP,QAAOA,qBAAAA,cAAcQ,GAAG,CAACD,qBAAlBP,qBAA+BS,OAAOC,gBAAgB;AAC/D;AAEA,kDAAkD,GAClD,SAASC,cAAcC,CAAS,EAAEC,CAAS;IACzC,OAAOD,IAAIC,IAAI,CAAC,IAAID,IAAIC,IAAI,IAAI;AAClC;AAEA;;;;;CAKC,GACD,OAAO,SAASC,8BACdC,WAAkC;QAEVA,uBACNA;IADlB,MAAMR,WAAWS,QAAOD,wBAAAA,YAAYR,QAAQ,YAApBQ,wBAAwB,IAAIE,IAAI;IACxD,MAAMZ,KAAKW,QAAOD,kBAAAA,YAAYV,EAAE,YAAdU,kBAAkB,IAAIE,IAAI;IAC5C,IAAI,CAACV,YAAY,CAACF,IAAI;QACpB,MAAM,IAAIa,MAAM;IAClB;IACA,MAAMC,WACJ,OAAOJ,YAAYI,QAAQ,KAAK,aAAaJ,YAAYI,QAAQ,GAAGC;IACtE,MAAMC,cACJ,OAAON,YAAYM,WAAW,KAAK,aAC/BN,YAAYM,WAAW,GACvBD;IACN,IAAI,CAACD,YAAY,CAACE,aAAa;QAC7B,MAAM,IAAIH,MACR,CAAC,yBAAyB,EAAEX,SAAS,CAAC,EAAEF,GAAG,0BAA0B,CAAC,GACpE;IAEN;IACA,MAAMiB,QAA+B;QACnCf;QACAF;OACIc,WAAW;QAAEA;IAAS,IAAI,CAAC,GAC3BE,cAAc;QAAEA;IAAY,IAAI,CAAC;IAEvC,MAAMjB,QAAQL,cAAcwB,SAAS,CACnC,CAACC,WAAaA,SAASjB,QAAQ,KAAKA,YAAYiB,SAASnB,EAAE,KAAKA;IAElE,IAAID,SAAS,GAAGL,aAAa,CAACK,MAAM,GAAGkB;SAClCvB,cAAc0B,IAAI,CAACH;AAC1B;AAEA,oEAAoE,GACpE,OAAO,SAASI;IACd,OAAO3B,cACJG,GAAG,CAAC,CAACoB,OAAOK,WAAc,CAAA;YAAEL;YAAOK;QAAS,CAAA,GAC5CC,IAAI,CACH,CAAChB,GAAGC,IACFP,YAAYM,EAAEU,KAAK,CAACf,QAAQ,IAAID,YAAYO,EAAES,KAAK,CAACf,QAAQ,KAC5DI,cAAcC,EAAEU,KAAK,CAACf,QAAQ,EAAEM,EAAES,KAAK,CAACf,QAAQ,KAChDK,EAAEe,QAAQ,GAAGd,EAAEc,QAAQ,EAE1BzB,GAAG,CAAC,CAAC,EAAEoB,KAAK,EAAE,GAAM,aAAKA;AAC9B;AAEA,yCAAyC,GACzC,OAAO,SAASO;IACd9B,cAAc+B,MAAM,GAAG;AACzB"}
|