@aglyn/aglyn 1.0.0-beta.263 → 1.0.0-beta.266
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-acquisition.d.ts +13 -0
- package/src/lib/app-utils/account-acquisition.js +84 -0
- package/src/lib/app-utils/account-acquisition.js.map +1 -1
- package/src/lib/app-utils/analytics-events.d.ts +70 -0
- package/src/lib/app-utils/analytics-events.js +8 -0
- package/src/lib/app-utils/analytics-events.js.map +1 -1
- package/src/lib/app-utils/docs-help-sections.generated.js +4 -1
- package/src/lib/app-utils/docs-help-sections.generated.js.map +1 -1
- package/src/lib/app-utils/docs-help.generated.d.ts +2 -2
- package/src/lib/app-utils/docs-help.generated.js +8 -1
- package/src/lib/app-utils/docs-help.generated.js.map +1 -1
- package/src/lib/app-utils/docs-index.generated.js +31 -10
- package/src/lib/app-utils/docs-index.generated.js.map +1 -1
- package/src/lib/app-utils/expand-repeatables.js +14 -7
- package/src/lib/app-utils/expand-repeatables.js.map +1 -1
- package/src/lib/app-utils/health-report.d.ts +47 -7
- package/src/lib/app-utils/health-report.js +31 -3
- package/src/lib/app-utils/health-report.js.map +1 -1
- package/src/lib/app-utils/live-document-count.client.d.ts +28 -0
- package/src/lib/app-utils/live-document-count.client.js +35 -0
- package/src/lib/app-utils/live-document-count.client.js.map +1 -0
- package/src/lib/app-utils/live-document-count.d.ts +91 -0
- package/src/lib/app-utils/live-document-count.js +81 -0
- package/src/lib/app-utils/live-document-count.js.map +1 -0
- package/src/lib/app-utils/native-app-web-view.d.ts +63 -0
- package/src/lib/app-utils/native-app-web-view.js +83 -0
- package/src/lib/app-utils/native-app-web-view.js.map +1 -0
- package/src/lib/app-utils/notification-sounds.d.ts +49 -0
- package/src/lib/app-utils/notification-sounds.js +54 -0
- package/src/lib/app-utils/notification-sounds.js.map +1 -0
- package/src/lib/app-utils/notifications.d.ts +2 -0
- package/src/lib/app-utils/notifications.js +32 -0
- package/src/lib/app-utils/notifications.js.map +1 -1
- package/src/lib/app-utils/screen-analytics-aggregate.d.ts +10 -0
- package/src/lib/app-utils/screen-analytics-aggregate.js +11 -0
- package/src/lib/app-utils/screen-analytics-aggregate.js.map +1 -1
- package/src/lib/plugin-manager/first-party-plugins.generated.js +6 -0
- package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
- package/src/lib/plugin-manager/plugin-activity-actions.d.ts +14 -0
- package/src/lib/plugin-manager/plugin-activity-actions.js +30 -3
- package/src/lib/plugin-manager/plugin-activity-actions.js.map +1 -1
|
@@ -127,6 +127,14 @@ export interface PluginActivityRegistration {
|
|
|
127
127
|
* type needs no entry — the feed reads its noun off the type itself.
|
|
128
128
|
*/
|
|
129
129
|
targetTypes?: Readonly<Record<string, string>>;
|
|
130
|
+
/**
|
|
131
|
+
* Words for the plugin's rows that store a SENTENCE rather than a code
|
|
132
|
+
* (AGL-3892): rows its writers filed before codes existed, or still file as
|
|
133
|
+
* prose (`Action ran on formSubmission`). Answers what a person reads for
|
|
134
|
+
* such an action, or `undefined` for one that is not the plugin's. Pure and
|
|
135
|
+
* cheap: every feed row asks every plugin that declares one.
|
|
136
|
+
*/
|
|
137
|
+
describeAction?: (action: string) => string | undefined;
|
|
130
138
|
}
|
|
131
139
|
/**
|
|
132
140
|
* The most codes one group may hold. A group's chip asks `action is any of`
|
|
@@ -169,6 +177,12 @@ export declare function pluginStaffAuditCollection(collection: string): {
|
|
|
169
177
|
export declare function isPluginJobTargetType(type: unknown): boolean;
|
|
170
178
|
/** The readable label for a declared code; `undefined` for any other action. */
|
|
171
179
|
export declare function pluginActivityActionLabel(action: unknown): string | undefined;
|
|
180
|
+
/**
|
|
181
|
+
* What a plugin's `describeAction` says of a stored sentence (AGL-3892), or
|
|
182
|
+
* `undefined` when no plugin claims it. A describer that throws is skipped,
|
|
183
|
+
* so one plugin's bug never blanks another's rows.
|
|
184
|
+
*/
|
|
185
|
+
export declare function pluginActivityDescribedAction(action: unknown): string | undefined;
|
|
172
186
|
/** The group a stored action's code belongs to, or `undefined`. */
|
|
173
187
|
export declare function pluginActivityGroupForAction(action: unknown): PluginActivityGroup | undefined;
|
|
174
188
|
/**
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
* Declared at module scope from both the client barrel and the `/server`
|
|
29
29
|
* entry (the AGL-428 pattern), with an explicit `pluginId`: module scope
|
|
30
30
|
* runs before the loader's ownership marker is set.
|
|
31
|
-
*/
|
|
31
|
+
*/ var _globalScope_PLUGIN_ACTIVITY_ACTIONS_KEY;
|
|
32
|
+
/** A namespaced type as the log stores it: `pluginId:noun`, both parts plain. */ const PLUGIN_TARGET_TYPE = /^([a-z][a-z0-9-]*):([A-Za-z][A-Za-z0-9]*)$/;
|
|
32
33
|
/** Whether a stored target type is a plugin's namespaced one. */ export function isPluginActivityTargetType(type) {
|
|
33
34
|
return typeof type === 'string' && PLUGIN_TARGET_TYPE.test(type);
|
|
34
35
|
}
|
|
@@ -40,7 +41,15 @@
|
|
|
40
41
|
if (typeof type !== 'string') return undefined;
|
|
41
42
|
return (_PLUGIN_TARGET_TYPE_exec = PLUGIN_TARGET_TYPE.exec(type)) == null ? void 0 : _PLUGIN_TARGET_TYPE_exec[2];
|
|
42
43
|
}
|
|
43
|
-
|
|
44
|
+
/**
|
|
45
|
+
* The registrations live on `globalThis` under `Symbol.for`: each app's boot
|
|
46
|
+
* (`instrumentation.ts`) declares them and its routes read them, and Next
|
|
47
|
+
* compiles those apart, so a module-scoped map would be one copy per module
|
|
48
|
+
* graph: boot would fill its copy and a route would read an empty one
|
|
49
|
+
* (AGL-3412, AGL-3868).
|
|
50
|
+
*/ const PLUGIN_ACTIVITY_ACTIONS_KEY = Symbol.for('@aglyn/aglyn:plugin-activity-actions');
|
|
51
|
+
const globalScope = globalThis;
|
|
52
|
+
const registrations = (_globalScope_PLUGIN_ACTIVITY_ACTIONS_KEY = globalScope[PLUGIN_ACTIVITY_ACTIONS_KEY]) != null ? _globalScope_PLUGIN_ACTIVITY_ACTIONS_KEY : globalScope[PLUGIN_ACTIVITY_ACTIONS_KEY] = new Map();
|
|
44
53
|
/**
|
|
45
54
|
* The most codes one group may hold. A group's chip asks `action is any of`
|
|
46
55
|
* its codes, which the feeds' queries serve as one Firestore `in` — capped at
|
|
@@ -158,6 +167,23 @@ function ownerOfActivityAction(key) {
|
|
|
158
167
|
const match = registration.actions.find((entry)=>entry.key === action);
|
|
159
168
|
if (match) return match.label;
|
|
160
169
|
}
|
|
170
|
+
return pluginActivityDescribedAction(action);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* What a plugin's `describeAction` says of a stored sentence (AGL-3892), or
|
|
174
|
+
* `undefined` when no plugin claims it. A describer that throws is skipped,
|
|
175
|
+
* so one plugin's bug never blanks another's rows.
|
|
176
|
+
*/ export function pluginActivityDescribedAction(action) {
|
|
177
|
+
if (typeof action !== 'string' || !action.trim()) return undefined;
|
|
178
|
+
for (const registration of registrations.values()){
|
|
179
|
+
if (!registration.describeAction) continue;
|
|
180
|
+
try {
|
|
181
|
+
const words = registration.describeAction(action);
|
|
182
|
+
if (words) return words;
|
|
183
|
+
} catch (unused) {
|
|
184
|
+
// A describer that throws describes nothing.
|
|
185
|
+
}
|
|
186
|
+
}
|
|
161
187
|
return undefined;
|
|
162
188
|
}
|
|
163
189
|
/** The group a stored action's code belongs to, or `undefined`. */ export function pluginActivityGroupForAction(action) {
|
|
@@ -173,7 +199,8 @@ function ownerOfActivityAction(key) {
|
|
|
173
199
|
* The filter chips the feeds offer: one per registered group, each with the
|
|
174
200
|
* codes its chip keeps. What an `action isAnyOf …` request sends.
|
|
175
201
|
*/ export function listPluginActivityFilters() {
|
|
176
|
-
|
|
202
|
+
// A plugin that only describes prose rows (no codes) has no chip: its chip could keep nothing.
|
|
203
|
+
return listPluginActivityRegistrations().filter((entry)=>entry.actions.length > 0).map((entry)=>({
|
|
177
204
|
group: entry.group,
|
|
178
205
|
actions: entry.actions.map((action)=>action.key)
|
|
179
206
|
}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-activity-actions.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 * Plugin-declared activity actions (AGL-2940).\n *\n * Most rows in `orgs/{orgId}/activity` and `hosts/{hostId}/activity` store\n * a prose action written at the call site. A plugin whose rows are read by\n * more than a person — a filter chip, a staff facet, an actor table — stores\n * a CODE (`ai.job.output`) and declares here what the code means: the label\n * a reader sees, the scope it is written at, and the group its filter chip\n * belongs to. The activity renderers read this catalog for labels and\n * filters, so a second plugin with coded rows gets a chip and a facet group\n * by registering, with no edit to the renderers.\n *\n * Declared at module scope from both the client barrel and the `/server`\n * entry (the AGL-428 pattern), with an explicit `pluginId`: module scope\n * runs before the loader's ownership marker is set.\n */\n\nexport type PluginActivityScope = 'org' | 'host' | 'staff'\n\n/**\n * A resource a PLUGIN files an org activity row under (AGL-2978): the\n * plugin's id, a colon and the plugin's own noun — `acme-mail:mailbox`.\n *\n * Core's activity targets are a closed list of core's own resources. A\n * plugin's resource is not one of them and is never added to that list; the\n * org log's target type accepts any namespaced type instead, and the feed\n * reads the noun after the colon for a reader. The namespace keeps two\n * plugins' nouns from meaning one thing.\n */\nexport type PluginActivityTargetType = `${string}:${string}`\n\n/** A namespaced type as the log stores it: `pluginId:noun`, both parts plain. */\nconst PLUGIN_TARGET_TYPE = /^([a-z][a-z0-9-]*):([A-Za-z][A-Za-z0-9]*)$/\n\n/** Whether a stored target type is a plugin's namespaced one. */\nexport function isPluginActivityTargetType(type: unknown): type is PluginActivityTargetType {\n return typeof type === 'string' && PLUGIN_TARGET_TYPE.test(type)\n}\n\n/**\n * The noun a plugin's namespaced type names — `acme-mail:mailbox` →\n * `mailbox` — or `undefined` for any other type.\n */\nexport function pluginActivityTargetNoun(type: unknown): string | undefined {\n if (typeof type !== 'string') return undefined\n return PLUGIN_TARGET_TYPE.exec(type)?.[2]\n}\n\nexport interface PluginActivityAction {\n /** The stored action code — dotted, stable, never a sentence. */\n key: string\n /** What a person reads for the code in the feed and the actor table. */\n label: string\n /**\n * The same act as a sentence about its target (AGL-3660): `{target}` is\n * replaced by the target's noun and name — `Created {target} with Aglyn AI`\n * reads `Created page Home with Aglyn AI`. Absent, a row reads `label`.\n */\n sentence?: string\n /** Which log(s) the writers put the code in. */\n scope: PluginActivityScope | readonly PluginActivityScope[]\n /** The feed target types rows carrying this code file their output under. */\n resourceTypes?: readonly string[]\n}\n\n/**\n * The filter a plugin's coded rows share: one chip in the org feed and the\n * actor table, one facet group on the staff audit page.\n */\nexport interface PluginActivityGroup {\n /** Stable group id — the facet's value (`ai`). */\n id: string\n /** The chip and the facet option (`AI`). */\n label: string\n /**\n * Staff audit actions that belong to the group without carrying one of the\n * registered codes — rows written by core on the plugin's behalf, matched\n * by prefix (`billing.assistOverage.`).\n */\n staffAuditPrefixes?: readonly string[]\n /**\n * Staff audit actions a plugin's staff doors write when staff READ\n * something rather than change it (AGL-2939): the audit log files them as\n * an access. Matched exactly.\n */\n staffAuditAccessActions?: readonly string[]\n /**\n * What a person reads for each staff audit code the plugin's doors write\n * (AGL-3660) — the audit trail shows these words and keeps the code for\n * the row's tooltip and details. Matched exactly.\n */\n staffAuditLabels?: Readonly<Record<string, string>>\n /**\n * A staff audit row's words drawn from what the act left (AGL-3660): the\n * plugin reads its own `after` (what a job made, from what) and answers\n * the sentence and the outcome. `undefined` falls back to the label.\n */\n staffAuditDescribe?: (entry: {\n action: string\n after: Readonly<Record<string, unknown>>\n }) => { action?: string; result?: string } | undefined\n /**\n * The plugin's collections as an audit target path walks them, named for\n * a reader (AGL-3660). `job` marks the collection whose id is the job an\n * activity row links to.\n */\n staffAuditCollections?: Readonly<Record<string, { noun: string; job?: boolean }>>\n /** Activity target types whose id is such a job (AGL-3660). */\n jobTargetTypes?: readonly string[]\n}\n\nexport interface PluginActivityRegistration {\n pluginId: string\n group: PluginActivityGroup\n actions: readonly PluginActivityAction[]\n /**\n * Target types the plugin's rows are filed under that are NOT namespaced,\n * with the noun a reader sees for each (AGL-3080): rows written before\n * `pluginId:noun` existed, whose stored type cannot change. A namespaced\n * type needs no entry — the feed reads its noun off the type itself.\n */\n targetTypes?: Readonly<Record<string, string>>\n}\n\nconst registrations = new Map<string, PluginActivityRegistration>()\n\n/**\n * The most codes one group may hold. A group's chip asks `action is any of`\n * its codes, which the feeds' queries serve as one Firestore `in` — capped at\n * thirty values — so a larger group would be a chip the list always refuses.\n */\nexport const PLUGIN_ACTIVITY_GROUP_MAX_ACTIONS = 30\n\n/**\n * Idempotent per plugin — re-registration replaces that plugin's catalog. A\n * code already declared by a DIFFERENT plugin is refused for the whole\n * registration: two owners for one code would give the row two labels. A\n * group of more than `PLUGIN_ACTIVITY_GROUP_MAX_ACTIONS` codes is refused\n * too, since its chip could never be applied.\n */\nexport function registerPluginActivityActions(\n registration: PluginActivityRegistration,\n): void {\n const pluginId = registration.pluginId.trim()\n if (!pluginId) throw new Error('plugin activity actions need a pluginId')\n if (registration.actions.length > PLUGIN_ACTIVITY_GROUP_MAX_ACTIONS) {\n throw new Error(\n `activity group \"${registration.group.id}\" declares ` +\n `${registration.actions.length} actions; its filter chip can ask for at most ` +\n `${PLUGIN_ACTIVITY_GROUP_MAX_ACTIONS}, refused \"${pluginId}\"`,\n )\n }\n for (const action of registration.actions) {\n const owner = ownerOfActivityAction(action.key)\n if (owner && owner !== pluginId) {\n throw new Error(\n `activity action \"${action.key}\" is already declared by \"${owner}\"; ` +\n `refused \"${pluginId}\"`,\n )\n }\n }\n for (const type of Object.keys(registration.targetTypes ?? {})) {\n const owner = [...registrations.values()].find(\n (entry) => entry.pluginId !== pluginId && entry.targetTypes?.[type] !== undefined,\n )?.pluginId\n if (owner) {\n throw new Error(\n `activity target type \"${type}\" is already declared by \"${owner}\"; ` +\n `refused \"${pluginId}\"`,\n )\n }\n }\n registrations.set(pluginId, registration)\n}\n\n/**\n * The noun a plugin declared for one of its un-namespaced target types, or\n * `undefined` when no plugin declared the type.\n */\nexport function pluginActivityTargetLabel(type: unknown): string | undefined {\n if (typeof type !== 'string') return undefined\n for (const registration of registrations.values()) {\n const label = registration.targetTypes?.[type]\n if (label) return label\n }\n return undefined\n}\n\nfunction ownerOfActivityAction(key: string): string | undefined {\n for (const registration of registrations.values()) {\n if (registration.actions.some((action) => action.key === key)) {\n return registration.pluginId\n }\n }\n return undefined\n}\n\n/** Every registration, in registration order. */\nexport function listPluginActivityRegistrations(): PluginActivityRegistration[] {\n return [...registrations.values()]\n}\n\n/** Every declared action across plugins. */\nexport function listPluginActivityActions(): PluginActivityAction[] {\n return listPluginActivityRegistrations().flatMap((entry) => [...entry.actions])\n}\n\n/** A declared code's sentence template (`{target}` placeholder), if it has one. */\nexport function pluginActivityActionSentence(action: unknown): string | undefined {\n if (typeof action !== 'string') return undefined\n for (const registration of registrations.values()) {\n const match = registration.actions.find((entry) => entry.key === action)\n if (match) return match.sentence\n }\n return undefined\n}\n\n/** What a plugin declared a staff audit code reads as; `undefined` otherwise. */\nexport function pluginStaffAuditActionLabel(action: unknown): string | undefined {\n if (typeof action !== 'string') return undefined\n for (const registration of registrations.values()) {\n const label = registration.group.staffAuditLabels?.[action]\n if (label) return label\n }\n return undefined\n}\n\n/** A plugin's words for one staff audit row, from its `after`; `undefined` if none. */\nexport function pluginStaffAuditDescribe(\n action: string,\n after: Readonly<Record<string, unknown>>,\n): { action?: string; result?: string } | undefined {\n for (const registration of registrations.values()) {\n const described = registration.group.staffAuditDescribe?.({ action, after })\n if (described) return described\n }\n return undefined\n}\n\n/** How a plugin names one of its collections in an audit path, if it does. */\nexport function pluginStaffAuditCollection(\n collection: string,\n): { noun: string; job?: boolean } | undefined {\n for (const registration of registrations.values()) {\n const named = registration.group.staffAuditCollections?.[collection]\n if (named) return named\n }\n return undefined\n}\n\n/** Whether an activity target type names a plugin's job. */\nexport function isPluginJobTargetType(type: unknown): boolean {\n if (typeof type !== 'string') return false\n for (const registration of registrations.values()) {\n if (registration.group.jobTargetTypes?.includes(type)) return true\n }\n return false\n}\n\n/** The readable label for a declared code; `undefined` for any other action. */\nexport function pluginActivityActionLabel(action: unknown): string | undefined {\n if (typeof action !== 'string') return undefined\n for (const registration of registrations.values()) {\n const match = registration.actions.find((entry) => entry.key === action)\n if (match) return match.label\n }\n return undefined\n}\n\n/** The group a stored action's code belongs to, or `undefined`. */\nexport function pluginActivityGroupForAction(\n action: unknown,\n): PluginActivityGroup | undefined {\n if (typeof action !== 'string') return undefined\n for (const registration of registrations.values()) {\n if (registration.actions.some((entry) => entry.key === action)) {\n return registration.group\n }\n }\n return undefined\n}\n\n/**\n * The filter chips the feeds offer: one per registered group, each with the\n * codes its chip keeps. What an `action isAnyOf …` request sends.\n */\nexport function listPluginActivityFilters(): Array<{\n group: PluginActivityGroup\n actions: string[]\n}> {\n return listPluginActivityRegistrations().map((entry) => ({\n group: entry.group,\n actions: entry.actions.map((action) => action.key),\n }))\n}\n\n/**\n * The staff audit facet group for an action: a registered group when the\n * action is one of its codes or starts with one of its prefixes, else the\n * action's leading namespace (`billing`, `org`, `plugins`).\n */\nexport function pluginStaffAuditActionGroup(action: unknown): string {\n const text = typeof action === 'string' ? action.trim() : ''\n if (!text) return ''\n const byCode = pluginActivityGroupForAction(text)\n if (byCode) return byCode.id\n for (const registration of registrations.values()) {\n const prefixes = registration.group.staffAuditPrefixes ?? []\n if (prefixes.some((prefix) => text.startsWith(prefix))) {\n return registration.group.id\n }\n }\n const dot = text.indexOf('.')\n return dot > 0 ? text.slice(0, dot) : text\n}\n\n/** Whether a staff audit action is a read some plugin declared (AGL-2939). */\nexport function isPluginStaffAuditAccess(action: unknown): boolean {\n if (typeof action !== 'string' || !action) return false\n for (const registration of registrations.values()) {\n if (registration.group.staffAuditAccessActions?.includes(action)) return true\n }\n return false\n}\n\n/** How a facet group reads in the menu: a registered label, else the id. */\nexport function pluginStaffAuditActionGroupLabel(group: string): string {\n for (const registration of registrations.values()) {\n if (registration.group.id === group) return registration.group.label\n }\n return group\n}\n\n/** Test seam: forget every registration. */\nexport function resetPluginActivityActionsForTests(): void {\n registrations.clear()\n}\n"],"names":["PLUGIN_TARGET_TYPE","isPluginActivityTargetType","type","test","pluginActivityTargetNoun","undefined","exec","registrations","Map","PLUGIN_ACTIVITY_GROUP_MAX_ACTIONS","registerPluginActivityActions","registration","pluginId","trim","Error","actions","length","group","id","action","owner","ownerOfActivityAction","key","Object","keys","targetTypes","values","find","entry","set","pluginActivityTargetLabel","label","some","listPluginActivityRegistrations","listPluginActivityActions","flatMap","pluginActivityActionSentence","match","sentence","pluginStaffAuditActionLabel","staffAuditLabels","pluginStaffAuditDescribe","after","described","staffAuditDescribe","pluginStaffAuditCollection","collection","named","staffAuditCollections","isPluginJobTargetType","jobTargetTypes","includes","pluginActivityActionLabel","pluginActivityGroupForAction","listPluginActivityFilters","map","pluginStaffAuditActionGroup","text","byCode","prefixes","staffAuditPrefixes","prefix","startsWith","dot","indexOf","slice","isPluginStaffAuditAccess","staffAuditAccessActions","pluginStaffAuditActionGroupLabel","resetPluginActivityActionsForTests","clear"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-activity-actions.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 * Plugin-declared activity actions (AGL-2940).\n *\n * Most rows in `orgs/{orgId}/activity` and `hosts/{hostId}/activity` store\n * a prose action written at the call site. A plugin whose rows are read by\n * more than a person — a filter chip, a staff facet, an actor table — stores\n * a CODE (`ai.job.output`) and declares here what the code means: the label\n * a reader sees, the scope it is written at, and the group its filter chip\n * belongs to. The activity renderers read this catalog for labels and\n * filters, so a second plugin with coded rows gets a chip and a facet group\n * by registering, with no edit to the renderers.\n *\n * Declared at module scope from both the client barrel and the `/server`\n * entry (the AGL-428 pattern), with an explicit `pluginId`: module scope\n * runs before the loader's ownership marker is set.\n */\n\nexport type PluginActivityScope = 'org' | 'host' | 'staff'\n\n/**\n * A resource a PLUGIN files an org activity row under (AGL-2978): the\n * plugin's id, a colon and the plugin's own noun — `acme-mail:mailbox`.\n *\n * Core's activity targets are a closed list of core's own resources. A\n * plugin's resource is not one of them and is never added to that list; the\n * org log's target type accepts any namespaced type instead, and the feed\n * reads the noun after the colon for a reader. The namespace keeps two\n * plugins' nouns from meaning one thing.\n */\nexport type PluginActivityTargetType = `${string}:${string}`\n\n/** A namespaced type as the log stores it: `pluginId:noun`, both parts plain. */\nconst PLUGIN_TARGET_TYPE = /^([a-z][a-z0-9-]*):([A-Za-z][A-Za-z0-9]*)$/\n\n/** Whether a stored target type is a plugin's namespaced one. */\nexport function isPluginActivityTargetType(type: unknown): type is PluginActivityTargetType {\n return typeof type === 'string' && PLUGIN_TARGET_TYPE.test(type)\n}\n\n/**\n * The noun a plugin's namespaced type names — `acme-mail:mailbox` →\n * `mailbox` — or `undefined` for any other type.\n */\nexport function pluginActivityTargetNoun(type: unknown): string | undefined {\n if (typeof type !== 'string') return undefined\n return PLUGIN_TARGET_TYPE.exec(type)?.[2]\n}\n\nexport interface PluginActivityAction {\n /** The stored action code — dotted, stable, never a sentence. */\n key: string\n /** What a person reads for the code in the feed and the actor table. */\n label: string\n /**\n * The same act as a sentence about its target (AGL-3660): `{target}` is\n * replaced by the target's noun and name — `Created {target} with Aglyn AI`\n * reads `Created page Home with Aglyn AI`. Absent, a row reads `label`.\n */\n sentence?: string\n /** Which log(s) the writers put the code in. */\n scope: PluginActivityScope | readonly PluginActivityScope[]\n /** The feed target types rows carrying this code file their output under. */\n resourceTypes?: readonly string[]\n}\n\n/**\n * The filter a plugin's coded rows share: one chip in the org feed and the\n * actor table, one facet group on the staff audit page.\n */\nexport interface PluginActivityGroup {\n /** Stable group id — the facet's value (`ai`). */\n id: string\n /** The chip and the facet option (`AI`). */\n label: string\n /**\n * Staff audit actions that belong to the group without carrying one of the\n * registered codes — rows written by core on the plugin's behalf, matched\n * by prefix (`billing.assistOverage.`).\n */\n staffAuditPrefixes?: readonly string[]\n /**\n * Staff audit actions a plugin's staff doors write when staff READ\n * something rather than change it (AGL-2939): the audit log files them as\n * an access. Matched exactly.\n */\n staffAuditAccessActions?: readonly string[]\n /**\n * What a person reads for each staff audit code the plugin's doors write\n * (AGL-3660) — the audit trail shows these words and keeps the code for\n * the row's tooltip and details. Matched exactly.\n */\n staffAuditLabels?: Readonly<Record<string, string>>\n /**\n * A staff audit row's words drawn from what the act left (AGL-3660): the\n * plugin reads its own `after` (what a job made, from what) and answers\n * the sentence and the outcome. `undefined` falls back to the label.\n */\n staffAuditDescribe?: (entry: {\n action: string\n after: Readonly<Record<string, unknown>>\n }) => { action?: string; result?: string } | undefined\n /**\n * The plugin's collections as an audit target path walks them, named for\n * a reader (AGL-3660). `job` marks the collection whose id is the job an\n * activity row links to.\n */\n staffAuditCollections?: Readonly<Record<string, { noun: string; job?: boolean }>>\n /** Activity target types whose id is such a job (AGL-3660). */\n jobTargetTypes?: readonly string[]\n}\n\nexport interface PluginActivityRegistration {\n pluginId: string\n group: PluginActivityGroup\n actions: readonly PluginActivityAction[]\n /**\n * Target types the plugin's rows are filed under that are NOT namespaced,\n * with the noun a reader sees for each (AGL-3080): rows written before\n * `pluginId:noun` existed, whose stored type cannot change. A namespaced\n * type needs no entry — the feed reads its noun off the type itself.\n */\n targetTypes?: Readonly<Record<string, string>>\n /**\n * Words for the plugin's rows that store a SENTENCE rather than a code\n * (AGL-3892): rows its writers filed before codes existed, or still file as\n * prose (`Action ran on formSubmission`). Answers what a person reads for\n * such an action, or `undefined` for one that is not the plugin's. Pure and\n * cheap: every feed row asks every plugin that declares one.\n */\n describeAction?: (action: string) => string | undefined\n}\n\n/**\n * The registrations live on `globalThis` under `Symbol.for`: each app's boot\n * (`instrumentation.ts`) declares them and its routes read them, and Next\n * compiles those apart, so a module-scoped map would be one copy per module\n * graph: boot would fill its copy and a route would read an empty one\n * (AGL-3412, AGL-3868).\n */\nconst PLUGIN_ACTIVITY_ACTIONS_KEY = Symbol.for('@aglyn/aglyn:plugin-activity-actions')\n\nconst globalScope = globalThis as typeof globalThis & {\n [PLUGIN_ACTIVITY_ACTIONS_KEY]?: Map<string, PluginActivityRegistration>\n}\n\nconst registrations: Map<string, PluginActivityRegistration> =\n globalScope[PLUGIN_ACTIVITY_ACTIONS_KEY] ??\n (globalScope[PLUGIN_ACTIVITY_ACTIONS_KEY] = new Map<string, PluginActivityRegistration>())\n\n/**\n * The most codes one group may hold. A group's chip asks `action is any of`\n * its codes, which the feeds' queries serve as one Firestore `in` — capped at\n * thirty values — so a larger group would be a chip the list always refuses.\n */\nexport const PLUGIN_ACTIVITY_GROUP_MAX_ACTIONS = 30\n\n/**\n * Idempotent per plugin — re-registration replaces that plugin's catalog. A\n * code already declared by a DIFFERENT plugin is refused for the whole\n * registration: two owners for one code would give the row two labels. A\n * group of more than `PLUGIN_ACTIVITY_GROUP_MAX_ACTIONS` codes is refused\n * too, since its chip could never be applied.\n */\nexport function registerPluginActivityActions(\n registration: PluginActivityRegistration,\n): void {\n const pluginId = registration.pluginId.trim()\n if (!pluginId) throw new Error('plugin activity actions need a pluginId')\n if (registration.actions.length > PLUGIN_ACTIVITY_GROUP_MAX_ACTIONS) {\n throw new Error(\n `activity group \"${registration.group.id}\" declares ` +\n `${registration.actions.length} actions; its filter chip can ask for at most ` +\n `${PLUGIN_ACTIVITY_GROUP_MAX_ACTIONS}, refused \"${pluginId}\"`,\n )\n }\n for (const action of registration.actions) {\n const owner = ownerOfActivityAction(action.key)\n if (owner && owner !== pluginId) {\n throw new Error(\n `activity action \"${action.key}\" is already declared by \"${owner}\"; ` +\n `refused \"${pluginId}\"`,\n )\n }\n }\n for (const type of Object.keys(registration.targetTypes ?? {})) {\n const owner = [...registrations.values()].find(\n (entry) => entry.pluginId !== pluginId && entry.targetTypes?.[type] !== undefined,\n )?.pluginId\n if (owner) {\n throw new Error(\n `activity target type \"${type}\" is already declared by \"${owner}\"; ` +\n `refused \"${pluginId}\"`,\n )\n }\n }\n registrations.set(pluginId, registration)\n}\n\n/**\n * The noun a plugin declared for one of its un-namespaced target types, or\n * `undefined` when no plugin declared the type.\n */\nexport function pluginActivityTargetLabel(type: unknown): string | undefined {\n if (typeof type !== 'string') return undefined\n for (const registration of registrations.values()) {\n const label = registration.targetTypes?.[type]\n if (label) return label\n }\n return undefined\n}\n\nfunction ownerOfActivityAction(key: string): string | undefined {\n for (const registration of registrations.values()) {\n if (registration.actions.some((action) => action.key === key)) {\n return registration.pluginId\n }\n }\n return undefined\n}\n\n/** Every registration, in registration order. */\nexport function listPluginActivityRegistrations(): PluginActivityRegistration[] {\n return [...registrations.values()]\n}\n\n/** Every declared action across plugins. */\nexport function listPluginActivityActions(): PluginActivityAction[] {\n return listPluginActivityRegistrations().flatMap((entry) => [...entry.actions])\n}\n\n/** A declared code's sentence template (`{target}` placeholder), if it has one. */\nexport function pluginActivityActionSentence(action: unknown): string | undefined {\n if (typeof action !== 'string') return undefined\n for (const registration of registrations.values()) {\n const match = registration.actions.find((entry) => entry.key === action)\n if (match) return match.sentence\n }\n return undefined\n}\n\n/** What a plugin declared a staff audit code reads as; `undefined` otherwise. */\nexport function pluginStaffAuditActionLabel(action: unknown): string | undefined {\n if (typeof action !== 'string') return undefined\n for (const registration of registrations.values()) {\n const label = registration.group.staffAuditLabels?.[action]\n if (label) return label\n }\n return undefined\n}\n\n/** A plugin's words for one staff audit row, from its `after`; `undefined` if none. */\nexport function pluginStaffAuditDescribe(\n action: string,\n after: Readonly<Record<string, unknown>>,\n): { action?: string; result?: string } | undefined {\n for (const registration of registrations.values()) {\n const described = registration.group.staffAuditDescribe?.({ action, after })\n if (described) return described\n }\n return undefined\n}\n\n/** How a plugin names one of its collections in an audit path, if it does. */\nexport function pluginStaffAuditCollection(\n collection: string,\n): { noun: string; job?: boolean } | undefined {\n for (const registration of registrations.values()) {\n const named = registration.group.staffAuditCollections?.[collection]\n if (named) return named\n }\n return undefined\n}\n\n/** Whether an activity target type names a plugin's job. */\nexport function isPluginJobTargetType(type: unknown): boolean {\n if (typeof type !== 'string') return false\n for (const registration of registrations.values()) {\n if (registration.group.jobTargetTypes?.includes(type)) return true\n }\n return false\n}\n\n/** The readable label for a declared code; `undefined` for any other action. */\nexport function pluginActivityActionLabel(action: unknown): string | undefined {\n if (typeof action !== 'string') return undefined\n for (const registration of registrations.values()) {\n const match = registration.actions.find((entry) => entry.key === action)\n if (match) return match.label\n }\n return pluginActivityDescribedAction(action)\n}\n\n/**\n * What a plugin's `describeAction` says of a stored sentence (AGL-3892), or\n * `undefined` when no plugin claims it. A describer that throws is skipped,\n * so one plugin's bug never blanks another's rows.\n */\nexport function pluginActivityDescribedAction(action: unknown): string | undefined {\n if (typeof action !== 'string' || !action.trim()) return undefined\n for (const registration of registrations.values()) {\n if (!registration.describeAction) continue\n try {\n const words = registration.describeAction(action)\n if (words) return words\n } catch {\n // A describer that throws describes nothing.\n }\n }\n return undefined\n}\n\n/** The group a stored action's code belongs to, or `undefined`. */\nexport function pluginActivityGroupForAction(\n action: unknown,\n): PluginActivityGroup | undefined {\n if (typeof action !== 'string') return undefined\n for (const registration of registrations.values()) {\n if (registration.actions.some((entry) => entry.key === action)) {\n return registration.group\n }\n }\n return undefined\n}\n\n/**\n * The filter chips the feeds offer: one per registered group, each with the\n * codes its chip keeps. What an `action isAnyOf …` request sends.\n */\nexport function listPluginActivityFilters(): Array<{\n group: PluginActivityGroup\n actions: string[]\n}> {\n // A plugin that only describes prose rows (no codes) has no chip: its chip could keep nothing.\n return listPluginActivityRegistrations()\n .filter((entry) => entry.actions.length > 0)\n .map((entry) => ({\n group: entry.group,\n actions: entry.actions.map((action) => action.key),\n }))\n}\n\n/**\n * The staff audit facet group for an action: a registered group when the\n * action is one of its codes or starts with one of its prefixes, else the\n * action's leading namespace (`billing`, `org`, `plugins`).\n */\nexport function pluginStaffAuditActionGroup(action: unknown): string {\n const text = typeof action === 'string' ? action.trim() : ''\n if (!text) return ''\n const byCode = pluginActivityGroupForAction(text)\n if (byCode) return byCode.id\n for (const registration of registrations.values()) {\n const prefixes = registration.group.staffAuditPrefixes ?? []\n if (prefixes.some((prefix) => text.startsWith(prefix))) {\n return registration.group.id\n }\n }\n const dot = text.indexOf('.')\n return dot > 0 ? text.slice(0, dot) : text\n}\n\n/** Whether a staff audit action is a read some plugin declared (AGL-2939). */\nexport function isPluginStaffAuditAccess(action: unknown): boolean {\n if (typeof action !== 'string' || !action) return false\n for (const registration of registrations.values()) {\n if (registration.group.staffAuditAccessActions?.includes(action)) return true\n }\n return false\n}\n\n/** How a facet group reads in the menu: a registered label, else the id. */\nexport function pluginStaffAuditActionGroupLabel(group: string): string {\n for (const registration of registrations.values()) {\n if (registration.group.id === group) return registration.group.label\n }\n return group\n}\n\n/** Test seam: forget every registration. */\nexport function resetPluginActivityActionsForTests(): void {\n registrations.clear()\n}\n"],"names":["globalScope","PLUGIN_TARGET_TYPE","isPluginActivityTargetType","type","test","pluginActivityTargetNoun","undefined","exec","PLUGIN_ACTIVITY_ACTIONS_KEY","Symbol","for","globalThis","registrations","Map","PLUGIN_ACTIVITY_GROUP_MAX_ACTIONS","registerPluginActivityActions","registration","pluginId","trim","Error","actions","length","group","id","action","owner","ownerOfActivityAction","key","Object","keys","targetTypes","values","find","entry","set","pluginActivityTargetLabel","label","some","listPluginActivityRegistrations","listPluginActivityActions","flatMap","pluginActivityActionSentence","match","sentence","pluginStaffAuditActionLabel","staffAuditLabels","pluginStaffAuditDescribe","after","described","staffAuditDescribe","pluginStaffAuditCollection","collection","named","staffAuditCollections","isPluginJobTargetType","jobTargetTypes","includes","pluginActivityActionLabel","pluginActivityDescribedAction","describeAction","words","pluginActivityGroupForAction","listPluginActivityFilters","filter","map","pluginStaffAuditActionGroup","text","byCode","prefixes","staffAuditPrefixes","prefix","startsWith","dot","indexOf","slice","isPluginStaffAuditAccess","staffAuditAccessActions","pluginStaffAuditActionGroupLabel","resetPluginActivityActionsForTests","clear"],"mappings":"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"}
|