@aglyn/aglyn 1.0.0-beta.183 → 1.0.0-beta.184
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/consent-groups.d.ts +23 -1
- package/src/lib/app-utils/consent-groups.js +29 -1
- package/src/lib/app-utils/consent-groups.js.map +1 -1
- package/src/lib/app-utils/docs-help.generated.d.ts +1 -1
- package/src/lib/app-utils/docs-help.generated.js +2 -0
- package/src/lib/app-utils/docs-help.generated.js.map +1 -1
- package/src/lib/app-utils/docs-index.generated.js +24 -10
- package/src/lib/app-utils/docs-index.generated.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/aglyn",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.184",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -33,16 +33,16 @@
|
|
|
33
33
|
"./package.json": "./package.json"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@aglyn/shared-data-enums": "1.0.0-beta.
|
|
37
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
38
|
-
"@aglyn/shared-data-types": "1.0.0-beta.
|
|
39
|
-
"@aglyn/shared-util-email": "1.0.0-beta.
|
|
40
|
-
"@aglyn/shared-util-first-touch": "1.0.0-beta.
|
|
41
|
-
"@aglyn/shared-util-http": "1.0.0-beta.
|
|
42
|
-
"@aglyn/shared-util-logger": "1.0.0-beta.
|
|
43
|
-
"@aglyn/shared-util-timestamp": "1.0.0-beta.
|
|
44
|
-
"@aglyn/shared-util-tools": "1.0.0-beta.
|
|
45
|
-
"@aglyn/shared-util-vendor": "1.0.0-beta.
|
|
36
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.184",
|
|
37
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.184",
|
|
38
|
+
"@aglyn/shared-data-types": "1.0.0-beta.184",
|
|
39
|
+
"@aglyn/shared-util-email": "1.0.0-beta.184",
|
|
40
|
+
"@aglyn/shared-util-first-touch": "1.0.0-beta.184",
|
|
41
|
+
"@aglyn/shared-util-http": "1.0.0-beta.184",
|
|
42
|
+
"@aglyn/shared-util-logger": "1.0.0-beta.184",
|
|
43
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.184",
|
|
44
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.184",
|
|
45
|
+
"@aglyn/shared-util-vendor": "1.0.0-beta.184",
|
|
46
46
|
"@data-driven-forms/react-form-renderer": "^4.2.0",
|
|
47
47
|
"@msgpack/msgpack": "^3.1.3",
|
|
48
48
|
"@types/unist": "^3.0.3",
|
|
@@ -64,7 +64,18 @@
|
|
|
64
64
|
* joining a group inherits every refusal already standing against it. The
|
|
65
65
|
* asymmetry is the same one the rest of this area keeps: a permissive fact is
|
|
66
66
|
* written narrowly and read exactly, a restrictive fact is read as broadly as
|
|
67
|
-
* it could possibly apply.
|
|
67
|
+
* it could possibly apply. Every opt-out the send paths consult is read this
|
|
68
|
+
* way — the site suppression lists, the topic opt-outs and the recipient's
|
|
69
|
+
* cadence (`email-suppression.ts`), and the consent refusal
|
|
70
|
+
* (`marketing-consent.ts`) — and the preference pages lift one across the
|
|
71
|
+
* group, since a person rejoining the sender has rejoined all of it.
|
|
72
|
+
*
|
|
73
|
+
* ⚠️ The one change that runs AGAINST reading is a site LEAVING a group. A
|
|
74
|
+
* refusal is stored on the site the person acted on, so once that site is no
|
|
75
|
+
* longer named, its former siblings stop seeing a refusal that was given to
|
|
76
|
+
* them too. Whatever edits this declaration must carry the leaving site's
|
|
77
|
+
* refusals onto the sites that stay — there is no console editor for it yet,
|
|
78
|
+
* and that is the rule the first one has to keep.
|
|
68
79
|
*
|
|
69
80
|
* ## VISIBILITY IS A SEPARATE AXIS
|
|
70
81
|
*
|
|
@@ -167,6 +178,17 @@ export declare function readConsentGroups(org: Record<string, unknown> | null |
|
|
|
167
178
|
* exists to make impossible.
|
|
168
179
|
*/
|
|
169
180
|
export declare function consentGroupForHost(org: Record<string, unknown> | null | undefined, hostId: string): ConsentGroup;
|
|
181
|
+
/**
|
|
182
|
+
* The sites whose OPT-OUTS answer for mail sent by `group.hostId`, the
|
|
183
|
+
* sending site first.
|
|
184
|
+
*
|
|
185
|
+
* Every site the group names, because a refusal filed against any of them is
|
|
186
|
+
* a refusal of the sender — see "OPT-OUT runs the other way" above. The
|
|
187
|
+
* sending site leads so a reader that treats its own record differently from
|
|
188
|
+
* a sibling's (a pending confirmation is the sending site's own concern) can
|
|
189
|
+
* tell them apart by position. A group of one is the site alone.
|
|
190
|
+
*/
|
|
191
|
+
export declare function consentGroupOptOutHosts(group: Pick<ConsentGroup, 'hostId' | 'hostIds'>): string[];
|
|
170
192
|
/**
|
|
171
193
|
* The `visibleTo` a resource captured by this group starts with.
|
|
172
194
|
*
|
|
@@ -63,7 +63,18 @@
|
|
|
63
63
|
* joining a group inherits every refusal already standing against it. The
|
|
64
64
|
* asymmetry is the same one the rest of this area keeps: a permissive fact is
|
|
65
65
|
* written narrowly and read exactly, a restrictive fact is read as broadly as
|
|
66
|
-
* it could possibly apply.
|
|
66
|
+
* it could possibly apply. Every opt-out the send paths consult is read this
|
|
67
|
+
* way — the site suppression lists, the topic opt-outs and the recipient's
|
|
68
|
+
* cadence (`email-suppression.ts`), and the consent refusal
|
|
69
|
+
* (`marketing-consent.ts`) — and the preference pages lift one across the
|
|
70
|
+
* group, since a person rejoining the sender has rejoined all of it.
|
|
71
|
+
*
|
|
72
|
+
* ⚠️ The one change that runs AGAINST reading is a site LEAVING a group. A
|
|
73
|
+
* refusal is stored on the site the person acted on, so once that site is no
|
|
74
|
+
* longer named, its former siblings stop seeing a refusal that was given to
|
|
75
|
+
* them too. Whatever edits this declaration must carry the leaving site's
|
|
76
|
+
* refusals onto the sites that stay — there is no console editor for it yet,
|
|
77
|
+
* and that is the rule the first one has to keep.
|
|
67
78
|
*
|
|
68
79
|
* ## VISIBILITY IS A SEPARATE AXIS
|
|
69
80
|
*
|
|
@@ -192,6 +203,23 @@
|
|
|
192
203
|
}
|
|
193
204
|
return soloConsentGroup(hostId);
|
|
194
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* The sites whose OPT-OUTS answer for mail sent by `group.hostId`, the
|
|
208
|
+
* sending site first.
|
|
209
|
+
*
|
|
210
|
+
* Every site the group names, because a refusal filed against any of them is
|
|
211
|
+
* a refusal of the sender — see "OPT-OUT runs the other way" above. The
|
|
212
|
+
* sending site leads so a reader that treats its own record differently from
|
|
213
|
+
* a sibling's (a pending confirmation is the sending site's own concern) can
|
|
214
|
+
* tell them apart by position. A group of one is the site alone.
|
|
215
|
+
*/ export function consentGroupOptOutHosts(group) {
|
|
216
|
+
var _group_hostIds;
|
|
217
|
+
const siblings = ((_group_hostIds = group.hostIds) != null ? _group_hostIds : []).filter((id)=>typeof id === 'string' && id && id !== group.hostId);
|
|
218
|
+
return [
|
|
219
|
+
group.hostId,
|
|
220
|
+
...new Set(siblings)
|
|
221
|
+
];
|
|
222
|
+
}
|
|
195
223
|
/**
|
|
196
224
|
* The `visibleTo` a resource captured by this group starts with.
|
|
197
225
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/consent-groups.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 * CONSENT GROUPS — the declared unit a marketing basis is given to.\n *\n * ## The two organizations this exists to serve at once\n *\n * An AGENCY runs twelve unrelated client brands out of one account. A person\n * who ticked a box on one client's form has agreed to hear from that client\n * and from nobody else, and mailing them on behalf of the other eleven is a\n * consent violation whatever the account structure says.\n *\n * A SINGLE BUSINESS runs three sites — a shop, a booking page, a blog — under\n * one name. It collects addresses on all three and mails them from any of\n * them, because there is one controller and one brand and the visitor knows\n * it. Refusing that is not caution, it is a product that cannot serve a\n * perfectly ordinary customer.\n *\n * The org boundary cannot tell them apart, and neither can anything else the\n * database already holds. So the difference is DECLARED: a group names the\n * sites that are one sender. Undeclared, every site is its own group of one,\n * which is the agency's answer and costs the agency nothing to get right.\n *\n * ## ⛔ NOTHING HERE IS INFERRED\n *\n * Not from shared ownership, not from a shared org, not from a shared sending\n * domain, not from a shared theme. Sharing an account is a billing fact.\n * There is exactly one input — {@link CONSENT_GROUPS_FIELD} on the org — and\n * a site that is not named in it is alone.\n *\n * ## A group must be DISCLOSABLE, so it must have a NAME\n *\n * Pooling is only legitimate if the person was told about it: the checkbox\n * has to say who they will hear from. A group with no display name cannot be\n * rendered on a capture surface, so it cannot be disclosed, so it does not\n * pool — {@link consentGroupForHost} returns the group of one for every site\n * in a nameless group rather than pooling silently. That refusal is the whole\n * mechanism by which \"declared\" is stronger than \"configured\".\n *\n * ## Pooling applies FORWARD only\n *\n * A grant records the group AS DISCLOSED at the moment it was given, in the\n * entry itself, and every covered site gets its own entry. Adding a site to a\n * group therefore reaches captures made after the change and none made\n * before. An org-level switch that widened existing grants would be the leak\n * wearing a different hat: the people already on the list were told a\n * different thing.\n *\n * OPT-OUT runs the other way — read against the CURRENT group, so a site\n * joining a group inherits every refusal already standing against it. The\n * asymmetry is the same one the rest of this area keeps: a permissive fact is\n * written narrowly and read exactly, a restrictive fact is read as broadly as\n * it could possibly apply.\n *\n * ## VISIBILITY IS A SEPARATE AXIS\n *\n * Seeing a contact is not permission to mail them. A group decides consent\n * and opt-out; `visibleTo` decides who can read the row, and an org may\n * widen that for reporting while consent stays where it was given. The two\n * are resolved by different functions on purpose, and neither reads the\n * other.\n */\n\nimport { hostScopeToken, MAX_SCOPE_HOSTS, type ScopeToken } from './scope-tokens'\n\n/**\n * The org field holding the declarations:\n * `{ [groupId]: { name, hostIds } }`.\n *\n * On the org rather than on each host, because a group is a statement ABOUT\n * a set and a set stored as N per-host pointers can disagree with itself —\n * site A naming a group that site B has left is a half-declared controller,\n * and there is no reading of that which is safe to pool on.\n */\nexport const CONSENT_GROUPS_FIELD = 'consentGroups'\n\n/**\n * The most sites one group may name.\n *\n * The same ceiling `visibleTo` carries, and for a related reason: the send\n * path reads a group's opt-out lists in one `getAll`, so the group multiplies\n * that round trip's size. It is also a sanity bound on a disclosure — a\n * checkbox that has to name forty brands has not disclosed anything.\n */\nexport const MAX_CONSENT_GROUP_HOSTS = MAX_SCOPE_HOSTS\n\n/** One declared group, as stored. */\nexport interface StoredConsentGroup {\n /** Shown on the capture surface. Without it the group cannot pool. */\n name: string\n /** The sites that are one sender. */\n hostIds: string[]\n}\n\n/**\n * The group a host belongs to, resolved FOR that host.\n *\n * It carries the asking site as well as the set, which is what lets one value\n * be passed where both are needed. Every consent read needs both — the grant\n * is looked up under the asking site, while a refusal is honored across the\n * whole group — and two arguments that must agree is two arguments that can\n * disagree.\n */\nexport interface ConsentGroup {\n /** The site this was resolved for. Always a member of {@link hostIds}. */\n hostId: string\n /**\n * The group's id, or the host's own id for an undeclared site.\n *\n * Recorded on every grant so a stored basis says which controller it was\n * given to even after the declaration changes.\n */\n groupId: string\n /**\n * What the capture surface must display. `null` for a group of one, where\n * the site's own name is what a form already shows and inventing a second\n * one would put a name in front of a person that nothing else uses.\n */\n name: string | null\n /** Every site covered, sorted, always including the host asked about. */\n hostIds: string[]\n /** False for the implicit group of one. */\n declared: boolean\n}\n\n/**\n * The group of one — a site that has declared no pooling.\n *\n * Exported because it is a value callers state deliberately: a writer that\n * means \"this site only\" says so, rather than passing a host id and letting\n * a helper decide what it covers.\n */\nexport function soloConsentGroup(hostId: string): ConsentGroup {\n if (!hostId) {\n throw new Error('[consent-groups] a consent group must name a site')\n }\n return {\n hostId,\n groupId: hostId,\n name: null,\n hostIds: [hostId],\n declared: false,\n }\n}\n\n/**\n * Reads the declared groups off an org document, dropping every entry that\n * cannot be honored.\n *\n * A malformed declaration reads as ABSENT, never as a wider group: the whole\n * value of this field is that pooling is deliberate, and a corrupt value must\n * not be a way to reach an audience nobody declared.\n *\n * The four refusals, each of which would otherwise pool without a disclosure:\n *\n * - **no usable name** — cannot be shown on a form, so cannot be disclosed;\n * - **fewer than two sites** — not a pooling declaration, and a group of one\n * is what an undeclared site already gets;\n * - **over {@link MAX_CONSENT_GROUP_HOSTS}** — see that constant;\n * - **a site claimed by two groups** — two controllers claiming one site is\n * a contradiction, not a wider group, so BOTH claims are dropped and the\n * site falls back to being alone.\n */\nexport function readConsentGroups(\n org: Record<string, unknown> | null | undefined,\n): Record<string, StoredConsentGroup> {\n const raw = (org ?? {})[CONSENT_GROUPS_FIELD]\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return {}\n const usable: Record<string, StoredConsentGroup> = {}\n for (const [groupId, value] of Object.entries(raw as Record<string, unknown>)) {\n if (!groupId || !value || typeof value !== 'object') continue\n const group = value as Record<string, unknown>\n const name = typeof group['name'] === 'string' ? group['name'].trim() : ''\n if (!name) continue\n const hostIds = Array.isArray(group['hostIds'])\n ? [\n ...new Set(\n (group['hostIds'] as unknown[])\n .map((id) => String(id ?? '').trim())\n .filter(Boolean),\n ),\n ].sort()\n : []\n if (hostIds.length < 2 || hostIds.length > MAX_CONSENT_GROUP_HOSTS) continue\n usable[groupId] = { name, hostIds }\n }\n /*\n * The overlap pass, second because it needs every survivor of the first.\n * A site named by two groups is dropped from BOTH — the alternative is\n * picking one, and a coin flip is not a declaration.\n */\n const claims = new Map<string, number>()\n for (const group of Object.values(usable)) {\n for (const hostId of group.hostIds) {\n claims.set(hostId, (claims.get(hostId) ?? 0) + 1)\n }\n }\n const contested = new Set(\n [...claims.entries()].filter(([, count]) => count > 1).map(([id]) => id),\n )\n if (!contested.size) return usable\n const settled: Record<string, StoredConsentGroup> = {}\n for (const [groupId, group] of Object.entries(usable)) {\n if (group.hostIds.some((hostId) => contested.has(hostId))) continue\n settled[groupId] = group\n }\n return settled\n}\n\n/**\n * The group `hostId` belongs to — the declared one, or the group of one.\n *\n * The ONLY way any caller learns what a site's consent covers. A caller that\n * built its own set from `org.hosts`, or from a sending domain, or from a\n * theme, would be inferring a controller, which is the thing this module\n * exists to make impossible.\n */\nexport function consentGroupForHost(\n org: Record<string, unknown> | null | undefined,\n hostId: string,\n): ConsentGroup {\n if (!hostId) {\n throw new Error('[consent-groups] a consent group must name a site')\n }\n for (const [groupId, group] of Object.entries(readConsentGroups(org))) {\n if (!group.hostIds.includes(hostId)) continue\n return {\n hostId,\n groupId,\n name: group.name,\n hostIds: [...group.hostIds],\n declared: true,\n }\n }\n return soloConsentGroup(hostId)\n}\n\n/**\n * The `visibleTo` a resource captured by this group starts with.\n *\n * A separate function from anything above, called by the capture doors and\n * by nothing that decides mailability, because visibility and consent are\n * different questions with different answers. A group of one produces\n * `['host:{id}']` — the isolation an agency needs, arrived at without the\n * agency configuring anything.\n */\nexport function consentGroupScope(group: ConsentGroup): ScopeToken[] {\n return group.hostIds.map(hostScopeToken)\n}\n\n/**\n * The sentence a capture surface must show beside the checkbox, or `null`\n * when the site's own name already says it.\n *\n * Returned as text rather than a boolean so that the disclosure and the\n * grant come from ONE resolved group: a form that rendered its own wording\n * from a separate lookup could show one set of brands and record another,\n * and the recorded set is the one that decides who mails this person.\n */\nexport function consentGroupDisclosure(group: ConsentGroup): string | null {\n if (!group.declared || !group.name) return null\n return `You'll receive marketing email from ${group.name}, which covers ${group.hostIds.length} sites.`\n}\n"],"names":["hostScopeToken","MAX_SCOPE_HOSTS","CONSENT_GROUPS_FIELD","MAX_CONSENT_GROUP_HOSTS","soloConsentGroup","hostId","Error","groupId","name","hostIds","declared","readConsentGroups","org","raw","Array","isArray","usable","value","Object","entries","group","trim","Set","map","id","String","filter","Boolean","sort","length","claims","Map","values","set","get","contested","count","size","settled","some","has","consentGroupForHost","includes","consentGroupScope","consentGroupDisclosure"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/consent-groups.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 * CONSENT GROUPS — the declared unit a marketing basis is given to.\n *\n * ## The two organizations this exists to serve at once\n *\n * An AGENCY runs twelve unrelated client brands out of one account. A person\n * who ticked a box on one client's form has agreed to hear from that client\n * and from nobody else, and mailing them on behalf of the other eleven is a\n * consent violation whatever the account structure says.\n *\n * A SINGLE BUSINESS runs three sites — a shop, a booking page, a blog — under\n * one name. It collects addresses on all three and mails them from any of\n * them, because there is one controller and one brand and the visitor knows\n * it. Refusing that is not caution, it is a product that cannot serve a\n * perfectly ordinary customer.\n *\n * The org boundary cannot tell them apart, and neither can anything else the\n * database already holds. So the difference is DECLARED: a group names the\n * sites that are one sender. Undeclared, every site is its own group of one,\n * which is the agency's answer and costs the agency nothing to get right.\n *\n * ## ⛔ NOTHING HERE IS INFERRED\n *\n * Not from shared ownership, not from a shared org, not from a shared sending\n * domain, not from a shared theme. Sharing an account is a billing fact.\n * There is exactly one input — {@link CONSENT_GROUPS_FIELD} on the org — and\n * a site that is not named in it is alone.\n *\n * ## A group must be DISCLOSABLE, so it must have a NAME\n *\n * Pooling is only legitimate if the person was told about it: the checkbox\n * has to say who they will hear from. A group with no display name cannot be\n * rendered on a capture surface, so it cannot be disclosed, so it does not\n * pool — {@link consentGroupForHost} returns the group of one for every site\n * in a nameless group rather than pooling silently. That refusal is the whole\n * mechanism by which \"declared\" is stronger than \"configured\".\n *\n * ## Pooling applies FORWARD only\n *\n * A grant records the group AS DISCLOSED at the moment it was given, in the\n * entry itself, and every covered site gets its own entry. Adding a site to a\n * group therefore reaches captures made after the change and none made\n * before. An org-level switch that widened existing grants would be the leak\n * wearing a different hat: the people already on the list were told a\n * different thing.\n *\n * OPT-OUT runs the other way — read against the CURRENT group, so a site\n * joining a group inherits every refusal already standing against it. The\n * asymmetry is the same one the rest of this area keeps: a permissive fact is\n * written narrowly and read exactly, a restrictive fact is read as broadly as\n * it could possibly apply. Every opt-out the send paths consult is read this\n * way — the site suppression lists, the topic opt-outs and the recipient's\n * cadence (`email-suppression.ts`), and the consent refusal\n * (`marketing-consent.ts`) — and the preference pages lift one across the\n * group, since a person rejoining the sender has rejoined all of it.\n *\n * ⚠️ The one change that runs AGAINST reading is a site LEAVING a group. A\n * refusal is stored on the site the person acted on, so once that site is no\n * longer named, its former siblings stop seeing a refusal that was given to\n * them too. Whatever edits this declaration must carry the leaving site's\n * refusals onto the sites that stay — there is no console editor for it yet,\n * and that is the rule the first one has to keep.\n *\n * ## VISIBILITY IS A SEPARATE AXIS\n *\n * Seeing a contact is not permission to mail them. A group decides consent\n * and opt-out; `visibleTo` decides who can read the row, and an org may\n * widen that for reporting while consent stays where it was given. The two\n * are resolved by different functions on purpose, and neither reads the\n * other.\n */\n\nimport { hostScopeToken, MAX_SCOPE_HOSTS, type ScopeToken } from './scope-tokens'\n\n/**\n * The org field holding the declarations:\n * `{ [groupId]: { name, hostIds } }`.\n *\n * On the org rather than on each host, because a group is a statement ABOUT\n * a set and a set stored as N per-host pointers can disagree with itself —\n * site A naming a group that site B has left is a half-declared controller,\n * and there is no reading of that which is safe to pool on.\n */\nexport const CONSENT_GROUPS_FIELD = 'consentGroups'\n\n/**\n * The most sites one group may name.\n *\n * The same ceiling `visibleTo` carries, and for a related reason: the send\n * path reads a group's opt-out lists in one `getAll`, so the group multiplies\n * that round trip's size. It is also a sanity bound on a disclosure — a\n * checkbox that has to name forty brands has not disclosed anything.\n */\nexport const MAX_CONSENT_GROUP_HOSTS = MAX_SCOPE_HOSTS\n\n/** One declared group, as stored. */\nexport interface StoredConsentGroup {\n /** Shown on the capture surface. Without it the group cannot pool. */\n name: string\n /** The sites that are one sender. */\n hostIds: string[]\n}\n\n/**\n * The group a host belongs to, resolved FOR that host.\n *\n * It carries the asking site as well as the set, which is what lets one value\n * be passed where both are needed. Every consent read needs both — the grant\n * is looked up under the asking site, while a refusal is honored across the\n * whole group — and two arguments that must agree is two arguments that can\n * disagree.\n */\nexport interface ConsentGroup {\n /** The site this was resolved for. Always a member of {@link hostIds}. */\n hostId: string\n /**\n * The group's id, or the host's own id for an undeclared site.\n *\n * Recorded on every grant so a stored basis says which controller it was\n * given to even after the declaration changes.\n */\n groupId: string\n /**\n * What the capture surface must display. `null` for a group of one, where\n * the site's own name is what a form already shows and inventing a second\n * one would put a name in front of a person that nothing else uses.\n */\n name: string | null\n /** Every site covered, sorted, always including the host asked about. */\n hostIds: string[]\n /** False for the implicit group of one. */\n declared: boolean\n}\n\n/**\n * The group of one — a site that has declared no pooling.\n *\n * Exported because it is a value callers state deliberately: a writer that\n * means \"this site only\" says so, rather than passing a host id and letting\n * a helper decide what it covers.\n */\nexport function soloConsentGroup(hostId: string): ConsentGroup {\n if (!hostId) {\n throw new Error('[consent-groups] a consent group must name a site')\n }\n return {\n hostId,\n groupId: hostId,\n name: null,\n hostIds: [hostId],\n declared: false,\n }\n}\n\n/**\n * Reads the declared groups off an org document, dropping every entry that\n * cannot be honored.\n *\n * A malformed declaration reads as ABSENT, never as a wider group: the whole\n * value of this field is that pooling is deliberate, and a corrupt value must\n * not be a way to reach an audience nobody declared.\n *\n * The four refusals, each of which would otherwise pool without a disclosure:\n *\n * - **no usable name** — cannot be shown on a form, so cannot be disclosed;\n * - **fewer than two sites** — not a pooling declaration, and a group of one\n * is what an undeclared site already gets;\n * - **over {@link MAX_CONSENT_GROUP_HOSTS}** — see that constant;\n * - **a site claimed by two groups** — two controllers claiming one site is\n * a contradiction, not a wider group, so BOTH claims are dropped and the\n * site falls back to being alone.\n */\nexport function readConsentGroups(\n org: Record<string, unknown> | null | undefined,\n): Record<string, StoredConsentGroup> {\n const raw = (org ?? {})[CONSENT_GROUPS_FIELD]\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return {}\n const usable: Record<string, StoredConsentGroup> = {}\n for (const [groupId, value] of Object.entries(raw as Record<string, unknown>)) {\n if (!groupId || !value || typeof value !== 'object') continue\n const group = value as Record<string, unknown>\n const name = typeof group['name'] === 'string' ? group['name'].trim() : ''\n if (!name) continue\n const hostIds = Array.isArray(group['hostIds'])\n ? [\n ...new Set(\n (group['hostIds'] as unknown[])\n .map((id) => String(id ?? '').trim())\n .filter(Boolean),\n ),\n ].sort()\n : []\n if (hostIds.length < 2 || hostIds.length > MAX_CONSENT_GROUP_HOSTS) continue\n usable[groupId] = { name, hostIds }\n }\n /*\n * The overlap pass, second because it needs every survivor of the first.\n * A site named by two groups is dropped from BOTH — the alternative is\n * picking one, and a coin flip is not a declaration.\n */\n const claims = new Map<string, number>()\n for (const group of Object.values(usable)) {\n for (const hostId of group.hostIds) {\n claims.set(hostId, (claims.get(hostId) ?? 0) + 1)\n }\n }\n const contested = new Set(\n [...claims.entries()].filter(([, count]) => count > 1).map(([id]) => id),\n )\n if (!contested.size) return usable\n const settled: Record<string, StoredConsentGroup> = {}\n for (const [groupId, group] of Object.entries(usable)) {\n if (group.hostIds.some((hostId) => contested.has(hostId))) continue\n settled[groupId] = group\n }\n return settled\n}\n\n/**\n * The group `hostId` belongs to — the declared one, or the group of one.\n *\n * The ONLY way any caller learns what a site's consent covers. A caller that\n * built its own set from `org.hosts`, or from a sending domain, or from a\n * theme, would be inferring a controller, which is the thing this module\n * exists to make impossible.\n */\nexport function consentGroupForHost(\n org: Record<string, unknown> | null | undefined,\n hostId: string,\n): ConsentGroup {\n if (!hostId) {\n throw new Error('[consent-groups] a consent group must name a site')\n }\n for (const [groupId, group] of Object.entries(readConsentGroups(org))) {\n if (!group.hostIds.includes(hostId)) continue\n return {\n hostId,\n groupId,\n name: group.name,\n hostIds: [...group.hostIds],\n declared: true,\n }\n }\n return soloConsentGroup(hostId)\n}\n\n/**\n * The sites whose OPT-OUTS answer for mail sent by `group.hostId`, the\n * sending site first.\n *\n * Every site the group names, because a refusal filed against any of them is\n * a refusal of the sender — see \"OPT-OUT runs the other way\" above. The\n * sending site leads so a reader that treats its own record differently from\n * a sibling's (a pending confirmation is the sending site's own concern) can\n * tell them apart by position. A group of one is the site alone.\n */\nexport function consentGroupOptOutHosts(\n group: Pick<ConsentGroup, 'hostId' | 'hostIds'>,\n): string[] {\n const siblings = (group.hostIds ?? []).filter(\n (id) => typeof id === 'string' && id && id !== group.hostId,\n )\n return [group.hostId, ...new Set(siblings)]\n}\n\n/**\n * The `visibleTo` a resource captured by this group starts with.\n *\n * A separate function from anything above, called by the capture doors and\n * by nothing that decides mailability, because visibility and consent are\n * different questions with different answers. A group of one produces\n * `['host:{id}']` — the isolation an agency needs, arrived at without the\n * agency configuring anything.\n */\nexport function consentGroupScope(group: ConsentGroup): ScopeToken[] {\n return group.hostIds.map(hostScopeToken)\n}\n\n/**\n * The sentence a capture surface must show beside the checkbox, or `null`\n * when the site's own name already says it.\n *\n * Returned as text rather than a boolean so that the disclosure and the\n * grant come from ONE resolved group: a form that rendered its own wording\n * from a separate lookup could show one set of brands and record another,\n * and the recorded set is the one that decides who mails this person.\n */\nexport function consentGroupDisclosure(group: ConsentGroup): string | null {\n if (!group.declared || !group.name) return null\n return `You'll receive marketing email from ${group.name}, which covers ${group.hostIds.length} sites.`\n}\n"],"names":["hostScopeToken","MAX_SCOPE_HOSTS","CONSENT_GROUPS_FIELD","MAX_CONSENT_GROUP_HOSTS","soloConsentGroup","hostId","Error","groupId","name","hostIds","declared","readConsentGroups","org","raw","Array","isArray","usable","value","Object","entries","group","trim","Set","map","id","String","filter","Boolean","sort","length","claims","Map","values","set","get","contested","count","size","settled","some","has","consentGroupForHost","includes","consentGroupOptOutHosts","siblings","consentGroupScope","consentGroupDisclosure"],"mappings":"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"}
|
|
@@ -305,7 +305,7 @@ export declare const PLUGIN_DOCS_ANCHORS: {
|
|
|
305
305
|
readonly crmReports: readonly ["#choosing-a-period", "#contacts", "#sources-and-lifecycle", "#conversion-by-source", "#lead-funnel", "#pipeline", "#forecast-by-close-month", "#won-and-lost", "#won-and-lost-by-owner", "#activity-by-teammate", "#tasks", "#exporting-a-table", "#crm-at-a-glance", "#how-the-numbers-are-counted", "#related"];
|
|
306
306
|
readonly crmSettings: readonly ["#companies", "#create-companies-from-work-email-domains", "#default-owner", "#assignment-rules", "#round-robin", "#email-templates", "#email-capture", "#your-sending-addresses", "#recipes", "#related"];
|
|
307
307
|
readonly crmTasks: readonly ["#the-tasks-page", "#the-calendar-view", "#snoozing-a-task", "#selecting-exporting-and-acting-on-many", "#import-from-csv", "#creating-a-task", "#assigning-a-task-to-someone-else", "#completing-and-reopening", "#organization-tasks", "#tasks-on-a-contact-company-or-deal", "#reminders", "#turning-reminders-off", "#next-activity", "#the-daily-digest", "#turning-it-off", "#the-dashboard-card", "#who-can-do-what", "#related"];
|
|
308
|
-
readonly crmViews: readonly ["#the-views-control", "#a-view-is-a-link", "#filters-on-the-contacts-list", "#columns-and-sort", "#segments-and-views", "#who-sees-what", "#related"];
|
|
308
|
+
readonly crmViews: readonly ["#the-views-control", "#a-view-is-a-link", "#filters", "#filters-on-the-contacts-list", "#filters-on-the-other-lists", "#columns-and-sort", "#segments-and-views", "#who-sees-what", "#related"];
|
|
309
309
|
readonly datasets: readonly ["#model-builder", "#typed-documents", "#relations", "#query-layer", "#repeatable-components", "#who-a-dataset-is-shared-with", "#import--export", "#related"];
|
|
310
310
|
readonly deals: readonly ["#pipelines", "#stages", "#the-board-and-the-table", "#import-from-csv", "#creating-a-deal", "#line-items", "#moving-winning-and-losing", "#a-won-deal-makes-its-contact-a-customer", "#a-deals-page", "#files", "#related"];
|
|
311
311
|
readonly designedEmails: readonly ["#create-a-template", "#duplicate-a-template", "#styling-email-blocks", "#merge-tokens", "#send-it", "#the-plain-text-version", "#start-from-a-brief-instead"];
|
|
@@ -671,7 +671,9 @@ export const PLUGIN_DOCS_ANCHORS = {
|
|
|
671
671
|
crmViews: [
|
|
672
672
|
'#the-views-control',
|
|
673
673
|
'#a-view-is-a-link',
|
|
674
|
+
'#filters',
|
|
674
675
|
'#filters-on-the-contacts-list',
|
|
676
|
+
'#filters-on-the-other-lists',
|
|
675
677
|
'#columns-and-sort',
|
|
676
678
|
'#segments-and-views',
|
|
677
679
|
'#who-sees-what',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/docs-help.generated.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// GENERATED FILE — do not edit. Regenerate with:\n// node tools/scripts/generate-docs-help.mjs\n// Source of truth: apps/docs/docs frontmatter + headings (AGL-602).\n\n// The first-party plugin consoles live in `libs/plugins/*` and cannot import\n// the console's constants, so they carry their own generated subset of the\n// docs help registry — the topics their cards actually link to, and no others.\n\nexport interface PluginDocsTopic {\n /** Docs-site path, e.g. `/commerce-and-bookings/commerce/overview`. */\n path: string\n /** Docs page title. */\n title: string\n /** Verbatim docs frontmatter description — the tooltip excerpt. */\n excerpt: string\n}\n\nexport const PLUGIN_DOCS = {\n abuseReports: {\n path: '/staff-console/abuse-reports',\n title: 'Abuse reports',\n excerpt: 'The public abuse-report queue — where outside reports land, how to triage by severity, which lever answers which report, and the CSAM and DMCA paths that are not takedown buttons.',\n },\n actionsBuilder: {\n path: '/marketing-and-automation/workflows-and-actions/actions-builder',\n title: 'Actions builder',\n excerpt: 'Map a single event to a single action without building a full workflow.',\n },\n aglynAssist: {\n path: '/getting-started/aglyn-assist',\n title: 'Aglyn Assist',\n excerpt: 'Ask the built-in AI helper how to do anything in Aglyn — it answers from these docs and links you straight to the right page.',\n },\n aiAutomations: {\n path: '/ai/automations-with-ai',\n title: 'Explain automations with AI',\n excerpt: 'Aglyn AI can explain an automation you already have and tell you why one of its runs failed. Drafting an automation from a description is not available yet.',\n },\n aiCrm: {\n path: '/ai/crm-by-ai',\n title: 'The AI CRM built into Aglyn',\n excerpt: 'An AI CRM for small business: a short summary and a suggested next step on a contact, company, deal or lead, a one-to-one email drafted into the composer, and an import\\'s columns matched to fields.',\n },\n aiExperiments: {\n path: '/ai/ab-tests-with-ai',\n title: 'A/B tests by AI: write variants, read the result',\n excerpt: 'Have Aglyn AI write two to four variants for a screen, section or email experiment, and put a finished test into plain language — with the verdict decided from the counts before the model is asked anything.',\n },\n aiMonitoring: {\n path: '/staff-console/ai-monitoring',\n title: 'AI monitoring',\n excerpt: 'How staff watch one organization\\'s AI usage — the add-on, credit pool, overage, refusals, jobs, tokens and cache hit rate, top spenders and margin — and where those figures appear across the staff console.',\n },\n aiProducts: {\n path: '/ai/products-with-ai',\n title: 'Product copy and catalogs with AI',\n excerpt: 'An AI product description generator for your catalog: write copy for one product or a whole import, propose a first catalog, and propose categories and discounts — all as proposals you review.',\n },\n aiSeo: {\n path: '/building-sites/seo/seo-by-ai',\n title: 'AI SEO for your website',\n excerpt: 'AI SEO for websites, on the pages you already have: have AI write a page or product\\'s search listing, audit every published page with a proposed fix for each finding, and draft your structured data and /llms.txt.',\n },\n aiThemes: {\n path: '/ai/theme-assist',\n title: 'Change your site\\'s theme with AI',\n excerpt: 'Describe a change to your site\\'s theme and get a proposal for the theme editor\\'s own controls, previewed before and after, that you save in the editor. Rolling out.',\n },\n assistSignals: {\n path: '/staff-console/assist-signals',\n title: 'Assist Signal',\n excerpt: 'The docs-gap and cost board behind Assist — how the ranking is ordered, why ungrounded questions are counted separately, and what the cache-read rate says about margin.',\n },\n billing: {\n path: '/workspace-and-billing/billing-and-plans/overview',\n title: 'Billing & Plans',\n excerpt: 'How Aglyn\\'s tiers, entitlements, quotas, usage meters, and seat add-ons work.',\n },\n bindings: {\n path: '/building-sites/bindings/overview',\n title: 'Bindings, Variables & Functions',\n excerpt: 'Live values in your content — typed variables, no-code functions, and rename-safe id tokens.',\n },\n bookings: {\n path: '/commerce-and-bookings/bookings/overview',\n title: 'Bookings & Scheduling',\n excerpt: 'Offer services with availability, let visitors book, take payment, and send reminders.',\n },\n buildAWorkflow: {\n path: '/marketing-and-automation/workflows-and-actions/build-a-workflow',\n title: 'Build a workflow',\n excerpt: 'Create a multi-step workflow that runs when a site event fires.',\n },\n catalog: {\n path: '/commerce-and-bookings/commerce/catalog',\n title: 'Product catalog',\n excerpt: 'Products with options and variants, categories, tags, and manual or smart collections.',\n },\n commerce: {\n path: '/commerce-and-bookings/commerce/overview',\n title: 'Commerce',\n excerpt: 'Sell physical, digital, and service products with a full catalog, orders pipeline, shipping, taxes, and your own Stripe account.',\n },\n commerceEndToEnd: {\n path: '/guides/commerce-end-to-end',\n title: 'Commerce end to end',\n excerpt: 'Create products with billing modes, design the storefront with Commerce blocks, take Stripe checkout for one-time and subscription sales, and run orders from the console.',\n },\n companies: {\n path: '/content-and-data/crm/companies',\n title: 'Companies',\n excerpt: 'Group your contacts under the businesses they belong to — one record per company, with its domain, owner, address and the people who work there.',\n },\n consoleTour: {\n path: '/getting-started/console-tour',\n title: 'The console tour',\n excerpt: 'Where things live in the Aglyn console app bar and navigation.',\n },\n contactActivities: {\n path: '/content-and-data/crm/activities',\n title: 'Activities & the timeline',\n excerpt: 'Log calls, emails, meetings and notes against a contact, a company, a deal or a lead, email one person from their record, and read it all in one timeline beside everything captured and every campaign sent.',\n },\n contactFields: {\n path: '/content-and-data/crm/custom-fields',\n title: 'Custom fields',\n excerpt: 'Define your own properties on contacts, companies, deals and leads — text, number, date, choice, checkbox or link — show them on every record and its list, and save form answers straight into a contact\\'s.',\n },\n contactRecord: {\n path: '/content-and-data/crm/contact-record',\n title: 'The contact record',\n excerpt: 'Add a contact by hand, keep a profile on them — phone, company, title, address, owner and lifecycle stage — open their own page in the CRM, and merge two records that turn out to be one person.',\n },\n contacts: {\n path: '/content-and-data/crm/overview',\n title: 'CRM',\n excerpt: 'One place for the people who interact with your sites — contacts captured from forms, members, orders and bookings, with leads, companies, a deals pipeline, tasks, a timeline, reports and custom fields.',\n },\n crmEmailTemplates: {\n path: '/content-and-data/crm/email-templates',\n title: 'Email templates',\n excerpt: 'Keep the letters your team sends from a record — templates that fill in the subject and the message, snippets that drop in a paragraph, and merge fields that fill in the person\\'s name, their company, the deal and you.',\n },\n crmLeads: {\n path: '/content-and-data/crm/leads',\n title: 'Leads',\n excerpt: 'Work the people your site has captured — a status, an owner and notes on every lead — and convert one into a contact, a company and a deal.',\n },\n crmReports: {\n path: '/content-and-data/crm/reports',\n title: 'Reports',\n excerpt: 'New contacts, where they came from and which sources convert, the lead funnel, the open pipeline and its forecast, won and lost, who logged what, and the task load — counted on the server, every table exportable as CSV.',\n },\n crmSettings: {\n path: '/content-and-data/crm/settings',\n title: 'CRM settings',\n excerpt: 'What the CRM does on its own for every site — whether a company is created from a contact\\'s work email domain, who a new contact is assigned to, the address that files replies on a record, and each site\\'s recipes.',\n },\n crmTasks: {\n path: '/content-and-data/crm/tasks',\n title: 'Tasks & follow-ups',\n excerpt: 'Calls, emails, meetings and to-dos with a due date, an assignee and a link to the record they are for — overdue and today read off the clock, a snooze, a reminder at its own time, and a morning digest.',\n },\n crmViews: {\n path: '/content-and-data/crm/views',\n title: 'Saved views',\n excerpt: 'Keep a CRM list\\'s filters, columns and sort under a name, open it from the views menu or a link, share it with the team, and use a contacts view as an email audience.',\n },\n datasets: {\n path: '/content-and-data/datasets/overview',\n title: 'Datasets & Dynamic Content',\n excerpt: 'Model structured content with typed fields and relations, then bind it into repeatable components.',\n },\n deals: {\n path: '/content-and-data/crm/deals',\n title: 'Deals pipeline',\n excerpt: 'Every open deal by stage — with an amount, an owner and an expected close — as a board you drag across or a table you page through, and the won and lost history behind it.',\n },\n designedEmails: {\n path: '/marketing-and-automation/email-campaigns/designed-emails',\n title: 'Designed emails',\n excerpt: 'Build campaign emails in the besigner with email-safe blocks and merge tokens — no separate editor.',\n },\n emailCampaigns: {\n path: '/marketing-and-automation/email-campaigns/overview',\n title: 'Email Campaigns',\n excerpt: 'Campaigns to audiences built from your contacts. Campaign email starts at Pro and needs a sending domain of the site\\'s own; receipts and account email send on every plan.',\n },\n events: {\n path: '/content-and-data/events/overview',\n title: 'Events Calendar',\n excerpt: 'Keep a schedule of events in the console and publish the ones you choose to any screen, with search-engine event markup.',\n },\n forms: {\n path: '/content-and-data/forms/overview',\n title: 'Forms & Lead Capture',\n excerpt: 'Add forms to your site, collect submissions in an inbox, and write them into datasets.',\n },\n installYourFirstPlugin: {\n path: '/guides/install-your-first-plugin',\n title: 'Install your first marketplace item',\n excerpt: 'A click-by-click walkthrough of the Marketplace — find something, choose which sites get it, install it, and turn it off again.',\n },\n inviteTeammates: {\n path: '/workspace-and-billing/teams-and-roles/invite-teammates',\n title: 'Invite teammates',\n excerpt: 'Add people to your site and understand how team members act within your organization.',\n },\n manifestAndEnvs: {\n path: '/developers/plugins/reference/manifest-and-envs',\n title: 'Manifests, trust lifecycle & environment',\n excerpt: 'The plugin manifest schema, the marketplace listing/version documents, the trust state machine, and every PLUGIN_* environment variable.',\n },\n marketingOverlays: {\n path: '/marketing-and-automation/marketing-overlays/overview',\n title: 'Marketing Overlays',\n excerpt: 'Site-wide announcement bars and promotional popups with triggers, scheduling, and email capture.',\n },\n membersOnly: {\n path: '/workspace-and-billing/teams-and-roles/members-only',\n title: 'Members-only areas',\n excerpt: 'Let visitors sign up as members and gate screens so only members can view them.',\n },\n orgAutomations: {\n path: '/marketing-and-automation/workflows-and-actions/org-automations',\n title: 'Org automations',\n excerpt: 'Write an automation once for the whole organization and run it on the sites you choose, with a pause on each site.',\n },\n plugins: {\n path: '/developers/plugins/overview',\n title: 'Plugins & Marketplace',\n excerpt: 'Extend Aglyn with sandboxed plugins — install from the marketplace, configure them, and publish your own.',\n },\n pos: {\n path: '/commerce-and-bookings/commerce/pos-and-reservations',\n title: 'POS & reservations',\n excerpt: 'Sell in person from the console register and take date-range reservations with deposits.',\n },\n publishAPlugin: {\n path: '/developers/plugins/publish-a-plugin',\n title: 'Publish a plugin',\n excerpt: 'Ship your own plugin to the marketplace with version pinning.',\n },\n publisherHandbook: {\n path: '/developers/plugins/publishing/publisher-handbook',\n title: 'Publisher handbook',\n excerpt: 'Publishing to the Aglyn marketplace — from profile setup through listing authoring, review, updates, and getting paid.',\n },\n redirects: {\n path: '/building-sites/redirects/overview',\n title: 'Redirects',\n excerpt: 'Manage URL redirects with validation, loop detection, and hit metrics.',\n },\n sandboxSecurity: {\n path: '/developers/plugins/reference/sandbox-security',\n title: 'Sandbox security model',\n excerpt: 'How sandboxed marketplace plugins are isolated — separate origin, per-manifest CSP, pinned artifacts — and what that means when you write one.',\n },\n sequences: {\n path: '/content-and-data/crm/sequences',\n title: 'Sequences',\n excerpt: 'One-to-one email sequences a rep sends to a person from their own connected mailbox, kept with the CRM. Rolling out.',\n },\n webhooks: {\n path: '/marketing-and-automation/workflows-and-actions/webhooks',\n title: 'Webhooks',\n excerpt: 'Connect Aglyn to other systems with outbound and inbound webhooks.',\n },\n} as const satisfies Record<string, PluginDocsTopic>\n\nexport type PluginDocsKey = keyof typeof PLUGIN_DOCS\n\nexport const PLUGIN_DOCS_ANCHORS = {\n abuseReports: ['#where-reports-come-from', '#triage-by-severity', '#csam', '#which-lever', '#statuses', '#disclosure', '#dmca', '#counter-notices', '#counter-notice-clock', '#counter-notice-steps', '#repeat-infringers', '#repeat-infringer-threshold', '#known-gaps', '#related'],\n actionsBuilder: ['#create-an-action', '#recipes', '#describe-it', '#triggers', '#crm-events', '#only-run-when-a-field-matches', '#chain-multiple-conditions-andor', '#steps', '#crm-steps', '#step-conditions', '#sequences', '#run-history', '#what-is-and-isnt-recorded', '#interactions-from-the-besigner', '#when-to-use-which', '#related'],\n aglynAssist: ['#what-it-can-do', '#aglyn-ai', '#answers-for-beginners-and-developers', '#offers-to-open-a-page', '#edits-in-the-besigner', '#where-an-answer-came-from', '#answers-straight-from-the-documentation', '#message-limits', '#feedback', '#privacy'],\n aiAutomations: ['#draft', '#explain', '#why-a-run-failed', '#what-is-sent', '#who-can-use-it', '#related'],\n aiCrm: ['#summarize-a-record', '#summaries-are-reused-until-the-record-changes', '#draft-an-email', '#match-columns', '#what-is-sent', '#who-can-use-it', '#related'],\n aiExperiments: ['#it-proposes-you-write', '#write-variants', '#putting-them-in', '#what-it-will-not-write', '#read-a-result', '#the-verdict', '#the-words', '#undecided', '#what-is-sent', '#who-can-use-it', '#related'],\n aiMonitoring: ['#the-ai-card', '#where-else', '#one-account', '#the-spend-leaderboard', '#alerts', '#related'],\n aiProducts: ['#write-a-products-copy', '#write-copy-for-many-products', '#when-you-import-products', '#propose-a-first-catalog', '#propose-categories-and-discounts', '#what-the-copy-never-says', '#what-is-sent-to-the-ai-provider', '#who-can-use-it', '#related'],\n aiSeo: ['#write-a-pages-listing', '#write-a-products-listing', '#audit-the-whole-site', '#target-keywords', '#apply-all-as-drafts', '#structured-data-and-llmstxt', '#related'],\n aiThemes: ['#change-what-you-describe-or-design-a-new-theme', '#match-your-brand', '#review-the-proposal', '#save-it-or-dont', '#who-can-use-it', '#related'],\n assistSignals: ['#the-workflow-this-board-exists-for', '#fleet', '#the-cache-read-rate-and-what-a-bad-number-looks-like', '#where-the-money-goes', '#tokens-by-kind', '#docs-gaps', '#questions-the-docs-could-not-answer', '#what-people-actually-asked', '#what-assist-costs-by-workspace', '#reading-the-sample-honestly', '#related'],\n billing: ['#tiers--entitlements', '#enterprise', '#single-sign-on-and-enforcement', '#usage-meters', '#who-is-generating-what', '#ai-allotments', '#storage-overage', '#if-you-would-rather-uploads-stopped', '#assist-overage', '#stop-ai-assist-at-the-included-band', '#ai-overage-ceiling', '#free-ai-credits', '#ai-credit-alerts', '#usage-budget', '#seats', '#crm-records', '#the-crm-suite', '#one-to-one-email', '#organization-data', '#api-access', '#payments', '#outstanding', '#plan-total', '#billing-email', '#payment-methods', '#billing-address', '#tax-ids', '#sales-tax', '#platform-fees', '#related'],\n bindings: ['#binding-tokens', '#rename-safe-id-tokens', '#insert-a-variable', '#token-pills', '#in-the-canvas-text-editor', '#site-details', '#typed-variables', '#no-code-functions', '#parameters-a-visitor-can-answer', '#a-calculator-you-lay-out', '#where-used--safety', '#workflows', '#related'],\n bookings: ['#set-up-bookings', '#taking-bookings', '#reminders', '#payments-and-fees', '#service-tax', '#manage', '#booking-from-the-crm', '#canceling-and-refunding', '#related'],\n buildAWorkflow: ['#1-open-the-workflows-page', '#2-choose-a-trigger', '#3-add-steps', '#waiting', '#4-save-and-test', '#duplicate-a-workflow', '#tips', '#related'],\n catalog: ['#products-options-and-variants', '#billing-modes-and-subscriptions', '#ai', '#categories-and-tags', '#collections', '#slugs', '#merchant-center-feed', '#related'],\n commerce: ['#products-hub', '#inventory', '#reserved-stock', '#stock-movements', '#gift-cards', '#recovery-and-alerts', '#orders', '#orders-screen', '#order-statuses', '#order-money-tiles', '#a-lost-dispute', '#shipping--taxes', '#lodging-tax-on-reservations', '#storefront-sales-tax', '#destination-coverage', '#dropshipping', '#related'],\n commerceEndToEnd: ['#1-connect-payments', '#2-create-products', '#3-design-the-storefront', '#catalog-search-filters-and-sort', '#category-pages', '#the-product-page-template', '#4-what-checkout-does', '#paying-without-leaving-your-site', '#5-run-orders-from-the-console', '#6-subscriptions--the-stripe-portal', '#related'],\n companies: ['#the-companies-list', '#create-a-company', '#a-companys-page', '#contacts-at-a-company', '#linked-on-capture', '#import-from-csv', '#export-csv', '#deleting-a-company', '#who-can-see-a-company', '#files', '#related'],\n consoleTour: ['#the-app-bar', '#in-context-help', '#primary-navigation', '#editing-vs-managing', '#the-sites-list', '#the-status-pill', '#how-the-pill-is-decided', '#your-site-allowance', '#a-sites-dashboard', '#next', '#workspace-settings--notifications', '#the-notifications-feed', '#notification-settings', '#one-kind-at-a-time', '#workspace-and-site-overrides', '#daily-digests', '#alerts-on-this-device'],\n contactActivities: ['#four-kinds-of-history', '#campaign-email', '#logging-an-activity', '#meeting-from-a-booking', '#click-to-call', '#sending-an-email', '#delivery-states', '#captured-email', '#where-an-activity-is-visible', '#the-recent-activity-feed', '#related'],\n contactFields: ['#define-a-field', '#fields-per-record', '#where-values-show', '#save-a-form-field', '#lead-source-values', '#over-the-api', '#retire-restore-delete', '#recompute-next-activity', '#related'],\n contactRecord: ['#adding-a-contact-by-hand', '#the-record-page', '#deleting-and-erasing', '#what-each-site-keeps-to-itself', '#merging-two-records', '#likely-duplicates', '#owner', '#last-engaged', '#lifecycle-stages', '#where-the-persons-lead-is', '#finding-a-contact', '#files', '#related'],\n contacts: ['#whats-in-the-crm-area', '#unified-ingestion', '#what-each-plan-includes', '#the-contacts-page', '#import-from-csv', '#segments', '#everywhere-the-crm-shows-up', '#capture-replies', '#at-the-organization-level', '#who-can-open-the-crm', '#related'],\n crmEmailTemplates: ['#templates-and-snippets', '#merge-fields', '#saving', '#managing-templates', '#duplicate-a-template', '#shared-or-personal', '#over-the-rest-api', '#related'],\n crmLeads: ['#what-makes-a-lead', '#what-a-lead-holds', '#adding-a-lead-by-hand', '#the-leads-list', '#working-a-lead-from-the-row', '#several-leads-at-once', '#export-csv', '#import-from-csv', '#who-owns-a-lead', '#a-leads-page', '#email-state', '#converting-a-lead', '#unqualifying-a-lead', '#erasing-the-person', '#who-can-do-this', '#related'],\n crmReports: ['#choosing-a-period', '#contacts', '#sources-and-lifecycle', '#conversion-by-source', '#lead-funnel', '#pipeline', '#forecast-by-close-month', '#won-and-lost', '#won-and-lost-by-owner', '#activity-by-teammate', '#tasks', '#exporting-a-table', '#crm-at-a-glance', '#how-the-numbers-are-counted', '#related'],\n crmSettings: ['#companies', '#create-companies-from-work-email-domains', '#default-owner', '#assignment-rules', '#round-robin', '#email-templates', '#email-capture', '#your-sending-addresses', '#recipes', '#related'],\n crmTasks: ['#the-tasks-page', '#the-calendar-view', '#snoozing-a-task', '#selecting-exporting-and-acting-on-many', '#import-from-csv', '#creating-a-task', '#assigning-a-task-to-someone-else', '#completing-and-reopening', '#organization-tasks', '#tasks-on-a-contact-company-or-deal', '#reminders', '#turning-reminders-off', '#next-activity', '#the-daily-digest', '#turning-it-off', '#the-dashboard-card', '#who-can-do-what', '#related'],\n crmViews: ['#the-views-control', '#a-view-is-a-link', '#filters-on-the-contacts-list', '#columns-and-sort', '#segments-and-views', '#who-sees-what', '#related'],\n datasets: ['#model-builder', '#typed-documents', '#relations', '#query-layer', '#repeatable-components', '#who-a-dataset-is-shared-with', '#import--export', '#related'],\n deals: ['#pipelines', '#stages', '#the-board-and-the-table', '#import-from-csv', '#creating-a-deal', '#line-items', '#moving-winning-and-losing', '#a-won-deal-makes-its-contact-a-customer', '#a-deals-page', '#files', '#related'],\n designedEmails: ['#create-a-template', '#duplicate-a-template', '#styling-email-blocks', '#merge-tokens', '#send-it', '#the-plain-text-version', '#start-from-a-brief-instead'],\n emailCampaigns: ['#send-a-campaign', '#campaigns-belong-to-the-organization', '#organization-emails-page', '#campaigns-group-emails', '#what-belongs-to-a-campaign', '#who-the-email-comes-from', '#sending-domains', '#account-email-always-sends', '#marketing-needs-a-domain', '#two-ways-to-get-a-domain', '#a-domain-we-set-up-is-a-request', '#domain-states', '#senders', '#send-a-test', '#preview-the-email', '#monthly-send-cap', '#personalize-with-merge-tags', '#recipient-count', '#who-a-campaign-is-allowed-to-reach', '#schedule-a-send', '#duplicate-an-email', '#email-lists', '#manual-lists', '#list-members', '#add-to-a-list', '#import-a-list', '#remove-from-a-list', '#lists-built-from-a-rule', '#experiments', '#experiments-across-sites', '#opens--clicks', '#the-campaign-report', '#per-contact-engagement', '#which-links-were-clicked', '#revenue-from-a-campaign', '#conversions', '#compliance', '#list-unsubscribe', '#topics', '#preference-page', '#frequency-opt-down', '#double-opt-in', '#marketing-mail', '#frequency-cap', '#suppressions', '#add-a-suppression', '#platform-suppressions', '#related'],\n events: ['#manage-events', '#show-events-on-a-screen', '#search-engines', '#related'],\n forms: ['#reading-submissions-from-code', '#build-a-form', '#monthly-allowance-per-plan', '#spam-and-abuse-protection', '#the-per-site-monthly-ceiling', '#field-types', '#labels-and-placeholders', '#example-a-quick-survey', '#after-submit', '#example-grow-an-email-list-from-a-signup-form', '#where-submissions-go', '#the-inbox', '#who-a-submission-is-from', '#where-this-one-went', '#replying-to-a-submission', '#every-sites-inbox-at-once', '#one-forms-own-page', '#duplicate-a-form', '#switch-forms-off-for-one-site', '#related'],\n installYourFirstPlugin: ['#before-you-start', '#step-1-open', '#step-2-browse', '#step-3-reviews', '#step-4-targeting', '#step-5-install', '#step-6-use', '#step-7-off', '#what-to-do-next', '#related'],\n inviteTeammates: ['#invite-someone', '#pending-invites', '#who-gets-told', '#accepting-an-invite', '#an-invitation-never-changes-who-owns-the-workspace', '#how-team-members-act', '#you-are-a-site-collaborators-support-channel', '#help-a-teammate-who-is-locked-out', '#why-you-cant-always-set-a-password', '#activity-log', '#ai-actions', '#ai-usage', '#ai-allotment', '#tips', '#related'],\n manifestAndEnvs: ['#plugin-manifest-published-with-every-version', '#contributes--where-the-plugin-loads', '#config--settings-without-writing-a-settings-screen', '#listing--version-documents', '#review--trust-lifecycle', '#environment-variables', '#pluginsconfigjson-first-party-contributors'],\n marketingOverlays: ['#announcement-bar', '#promotional-popups', '#frequency', '#popup-v2', '#multiple-overlays-scheduling--page-targeting', '#variables-in-copy', '#engagement-stats', '#across-your-sites', '#related'],\n membersOnly: ['#let-visitors-sign-up', '#sign-in-sign-up-and-recovery-pages', '#forgotten-passwords', '#gate-a-screen', '#manage-your-members', '#suspend-or-reactivate-a-member', '#tips', '#related'],\n orgAutomations: ['#what-an-org-automation-is', '#create-one', '#triggers', '#steps', '#pause-it-on-one-site', '#waiting-switching-off-and-deleting', '#every-sites-own-automations', '#related'],\n plugins: ['#install--upgrade', '#browse-card', '#whats-included', '#what-the-badges-on-a-listing-mean', '#how-plugins-run', '#when-one-plugin-depends-on-another', '#a-dependency-that-is-off-for-one-site', '#configure', '#configure-site', '#publish-your-own', '#related'],\n pos: ['#registers', '#the-register', '#platform-fees-at-the-register', '#selling-past-the-count', '#when-something-disconnects', '#reservations', '#related'],\n publishAPlugin: ['#the-publish-pipeline', '#private-plugins', '#paid-listings', '#your-publisher-profile', '#tips', '#related'],\n publisherHandbook: ['#before-your-first-publish', '#the-publisher-agreement', '#where-to-publish-from', '#what-installing-each-type-does', '#rules-an-email-starter-has-to-meet', '#publishing-a-version', '#before-you-publish', '#review-what-happens-after-you-publish', '#the-two-badges-and-what-each-one-promises', '#asking-to-be-verified', '#testing-a-version-before-it-is-approved', '#watching-your-own-submission', '#disabled-versions', '#private-plugins', '#authoring-your-listing', '#what-your-listing-can-say-about-aglyn', '#versioning--updates', '#shipping-a-new-version', '#how-installs-work-the-buyer-side', '#getting-paid', '#low-prices-and-processing'],\n redirects: ['#manage-redirects', '#sending-visitors-to-another-site', '#metrics', '#match-modes-v2', '#related'],\n sandboxSecurity: ['#a-separate-origin', '#per-manifest-network-policy', '#when-you-cant-declare-the-origin', '#pinned-immutable-artifacts', '#what-this-means-when-you-build', '#related'],\n sequences: ['#what-it-is-for', '#sent-from-your-own-mailbox', '#where-it-lives', '#connect-a-mailbox', '#send-as', '#daily-cap', '#mailbox-status', '#auto-pause', '#mailbox-actions', '#link-domains', '#compliance-settings', '#allowed-countries', '#do-not-contact-domains', '#sequences', '#build-a-sequence', '#sequence-status', '#enroll', '#cold-contacts', '#enrollments', '#sending', '#what-stops-a-sequence', '#unsubscribe', '#related'],\n webhooks: ['#outbound-webhooks', '#inbound-webhooks', '#tips', '#related'],\n} as const satisfies Partial<Record<PluginDocsKey, readonly `#${string}`[]>>\n\ntype PluginAnchorMap = typeof PLUGIN_DOCS_ANCHORS\n\n/** Valid heading anchors for a plugin docs page (`never` when none). */\nexport type PluginDocsAnchor<K extends PluginDocsKey> =\n K extends keyof PluginAnchorMap ? PluginAnchorMap[K][number] : never\n"],"names":["PLUGIN_DOCS","abuseReports","path","title","excerpt","actionsBuilder","aglynAssist","aiAutomations","aiCrm","aiExperiments","aiMonitoring","aiProducts","aiSeo","aiThemes","assistSignals","billing","bindings","bookings","buildAWorkflow","catalog","commerce","commerceEndToEnd","companies","consoleTour","contactActivities","contactFields","contactRecord","contacts","crmEmailTemplates","crmLeads","crmReports","crmSettings","crmTasks","crmViews","datasets","deals","designedEmails","emailCampaigns","events","forms","installYourFirstPlugin","inviteTeammates","manifestAndEnvs","marketingOverlays","membersOnly","orgAutomations","plugins","pos","publishAPlugin","publisherHandbook","redirects","sandboxSecurity","sequences","webhooks","PLUGIN_DOCS_ANCHORS"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD,iDAAiD;AACjD,8CAA8C;AAC9C,oEAAoE;AAEpE,6EAA6E;AAC7E,2EAA2E;AAC3E,+EAA+E;AAW/E,OAAO,MAAMA,cAAc;IACzBC,cAAc;QACZC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAC,gBAAgB;QACdH,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAE,aAAa;QACXJ,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAG,eAAe;QACbL,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAI,OAAO;QACLN,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAK,eAAe;QACbP,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAM,cAAc;QACZR,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAO,YAAY;QACVT,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAQ,OAAO;QACLV,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAS,UAAU;QACRX,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAU,eAAe;QACbZ,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAW,SAAS;QACPb,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAY,UAAU;QACRd,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAa,UAAU;QACRf,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAc,gBAAgB;QACdhB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAe,SAAS;QACPjB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAgB,UAAU;QACRlB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAiB,kBAAkB;QAChBnB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAkB,WAAW;QACTpB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAmB,aAAa;QACXrB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAoB,mBAAmB;QACjBtB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAqB,eAAe;QACbvB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAsB,eAAe;QACbxB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAuB,UAAU;QACRzB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAwB,mBAAmB;QACjB1B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAyB,UAAU;QACR3B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA0B,YAAY;QACV5B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA2B,aAAa;QACX7B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA4B,UAAU;QACR9B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA6B,UAAU;QACR/B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA8B,UAAU;QACRhC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA+B,OAAO;QACLjC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAgC,gBAAgB;QACdlC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAiC,gBAAgB;QACdnC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAkC,QAAQ;QACNpC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAmC,OAAO;QACLrC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAoC,wBAAwB;QACtBtC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAqC,iBAAiB;QACfvC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAsC,iBAAiB;QACfxC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAuC,mBAAmB;QACjBzC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAwC,aAAa;QACX1C,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAyC,gBAAgB;QACd3C,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA0C,SAAS;QACP5C,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA2C,KAAK;QACH7C,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA4C,gBAAgB;QACd9C,MAAM;QACNC,O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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/docs-help.generated.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// GENERATED FILE — do not edit. Regenerate with:\n// node tools/scripts/generate-docs-help.mjs\n// Source of truth: apps/docs/docs frontmatter + headings (AGL-602).\n\n// The first-party plugin consoles live in `libs/plugins/*` and cannot import\n// the console's constants, so they carry their own generated subset of the\n// docs help registry — the topics their cards actually link to, and no others.\n\nexport interface PluginDocsTopic {\n /** Docs-site path, e.g. `/commerce-and-bookings/commerce/overview`. */\n path: string\n /** Docs page title. */\n title: string\n /** Verbatim docs frontmatter description — the tooltip excerpt. */\n excerpt: string\n}\n\nexport const PLUGIN_DOCS = {\n abuseReports: {\n path: '/staff-console/abuse-reports',\n title: 'Abuse reports',\n excerpt: 'The public abuse-report queue — where outside reports land, how to triage by severity, which lever answers which report, and the CSAM and DMCA paths that are not takedown buttons.',\n },\n actionsBuilder: {\n path: '/marketing-and-automation/workflows-and-actions/actions-builder',\n title: 'Actions builder',\n excerpt: 'Map a single event to a single action without building a full workflow.',\n },\n aglynAssist: {\n path: '/getting-started/aglyn-assist',\n title: 'Aglyn Assist',\n excerpt: 'Ask the built-in AI helper how to do anything in Aglyn — it answers from these docs and links you straight to the right page.',\n },\n aiAutomations: {\n path: '/ai/automations-with-ai',\n title: 'Explain automations with AI',\n excerpt: 'Aglyn AI can explain an automation you already have and tell you why one of its runs failed. Drafting an automation from a description is not available yet.',\n },\n aiCrm: {\n path: '/ai/crm-by-ai',\n title: 'The AI CRM built into Aglyn',\n excerpt: 'An AI CRM for small business: a short summary and a suggested next step on a contact, company, deal or lead, a one-to-one email drafted into the composer, and an import\\'s columns matched to fields.',\n },\n aiExperiments: {\n path: '/ai/ab-tests-with-ai',\n title: 'A/B tests by AI: write variants, read the result',\n excerpt: 'Have Aglyn AI write two to four variants for a screen, section or email experiment, and put a finished test into plain language — with the verdict decided from the counts before the model is asked anything.',\n },\n aiMonitoring: {\n path: '/staff-console/ai-monitoring',\n title: 'AI monitoring',\n excerpt: 'How staff watch one organization\\'s AI usage — the add-on, credit pool, overage, refusals, jobs, tokens and cache hit rate, top spenders and margin — and where those figures appear across the staff console.',\n },\n aiProducts: {\n path: '/ai/products-with-ai',\n title: 'Product copy and catalogs with AI',\n excerpt: 'An AI product description generator for your catalog: write copy for one product or a whole import, propose a first catalog, and propose categories and discounts — all as proposals you review.',\n },\n aiSeo: {\n path: '/building-sites/seo/seo-by-ai',\n title: 'AI SEO for your website',\n excerpt: 'AI SEO for websites, on the pages you already have: have AI write a page or product\\'s search listing, audit every published page with a proposed fix for each finding, and draft your structured data and /llms.txt.',\n },\n aiThemes: {\n path: '/ai/theme-assist',\n title: 'Change your site\\'s theme with AI',\n excerpt: 'Describe a change to your site\\'s theme and get a proposal for the theme editor\\'s own controls, previewed before and after, that you save in the editor. Rolling out.',\n },\n assistSignals: {\n path: '/staff-console/assist-signals',\n title: 'Assist Signal',\n excerpt: 'The docs-gap and cost board behind Assist — how the ranking is ordered, why ungrounded questions are counted separately, and what the cache-read rate says about margin.',\n },\n billing: {\n path: '/workspace-and-billing/billing-and-plans/overview',\n title: 'Billing & Plans',\n excerpt: 'How Aglyn\\'s tiers, entitlements, quotas, usage meters, and seat add-ons work.',\n },\n bindings: {\n path: '/building-sites/bindings/overview',\n title: 'Bindings, Variables & Functions',\n excerpt: 'Live values in your content — typed variables, no-code functions, and rename-safe id tokens.',\n },\n bookings: {\n path: '/commerce-and-bookings/bookings/overview',\n title: 'Bookings & Scheduling',\n excerpt: 'Offer services with availability, let visitors book, take payment, and send reminders.',\n },\n buildAWorkflow: {\n path: '/marketing-and-automation/workflows-and-actions/build-a-workflow',\n title: 'Build a workflow',\n excerpt: 'Create a multi-step workflow that runs when a site event fires.',\n },\n catalog: {\n path: '/commerce-and-bookings/commerce/catalog',\n title: 'Product catalog',\n excerpt: 'Products with options and variants, categories, tags, and manual or smart collections.',\n },\n commerce: {\n path: '/commerce-and-bookings/commerce/overview',\n title: 'Commerce',\n excerpt: 'Sell physical, digital, and service products with a full catalog, orders pipeline, shipping, taxes, and your own Stripe account.',\n },\n commerceEndToEnd: {\n path: '/guides/commerce-end-to-end',\n title: 'Commerce end to end',\n excerpt: 'Create products with billing modes, design the storefront with Commerce blocks, take Stripe checkout for one-time and subscription sales, and run orders from the console.',\n },\n companies: {\n path: '/content-and-data/crm/companies',\n title: 'Companies',\n excerpt: 'Group your contacts under the businesses they belong to — one record per company, with its domain, owner, address and the people who work there.',\n },\n consoleTour: {\n path: '/getting-started/console-tour',\n title: 'The console tour',\n excerpt: 'Where things live in the Aglyn console app bar and navigation.',\n },\n contactActivities: {\n path: '/content-and-data/crm/activities',\n title: 'Activities & the timeline',\n excerpt: 'Log calls, emails, meetings and notes against a contact, a company, a deal or a lead, email one person from their record, and read it all in one timeline beside everything captured and every campaign sent.',\n },\n contactFields: {\n path: '/content-and-data/crm/custom-fields',\n title: 'Custom fields',\n excerpt: 'Define your own properties on contacts, companies, deals and leads — text, number, date, choice, checkbox or link — show them on every record and its list, and save form answers straight into a contact\\'s.',\n },\n contactRecord: {\n path: '/content-and-data/crm/contact-record',\n title: 'The contact record',\n excerpt: 'Add a contact by hand, keep a profile on them — phone, company, title, address, owner and lifecycle stage — open their own page in the CRM, and merge two records that turn out to be one person.',\n },\n contacts: {\n path: '/content-and-data/crm/overview',\n title: 'CRM',\n excerpt: 'One place for the people who interact with your sites — contacts captured from forms, members, orders and bookings, with leads, companies, a deals pipeline, tasks, a timeline, reports and custom fields.',\n },\n crmEmailTemplates: {\n path: '/content-and-data/crm/email-templates',\n title: 'Email templates',\n excerpt: 'Keep the letters your team sends from a record — templates that fill in the subject and the message, snippets that drop in a paragraph, and merge fields that fill in the person\\'s name, their company, the deal and you.',\n },\n crmLeads: {\n path: '/content-and-data/crm/leads',\n title: 'Leads',\n excerpt: 'Work the people your site has captured — a status, an owner and notes on every lead — and convert one into a contact, a company and a deal.',\n },\n crmReports: {\n path: '/content-and-data/crm/reports',\n title: 'Reports',\n excerpt: 'New contacts, where they came from and which sources convert, the lead funnel, the open pipeline and its forecast, won and lost, who logged what, and the task load — counted on the server, every table exportable as CSV.',\n },\n crmSettings: {\n path: '/content-and-data/crm/settings',\n title: 'CRM settings',\n excerpt: 'What the CRM does on its own for every site — whether a company is created from a contact\\'s work email domain, who a new contact is assigned to, the address that files replies on a record, and each site\\'s recipes.',\n },\n crmTasks: {\n path: '/content-and-data/crm/tasks',\n title: 'Tasks & follow-ups',\n excerpt: 'Calls, emails, meetings and to-dos with a due date, an assignee and a link to the record they are for — overdue and today read off the clock, a snooze, a reminder at its own time, and a morning digest.',\n },\n crmViews: {\n path: '/content-and-data/crm/views',\n title: 'Saved views',\n excerpt: 'Keep a CRM list\\'s filters, columns and sort under a name, open it from the views menu or a link, share it with the team, and use a contacts view as an email audience.',\n },\n datasets: {\n path: '/content-and-data/datasets/overview',\n title: 'Datasets & Dynamic Content',\n excerpt: 'Model structured content with typed fields and relations, then bind it into repeatable components.',\n },\n deals: {\n path: '/content-and-data/crm/deals',\n title: 'Deals pipeline',\n excerpt: 'Every open deal by stage — with an amount, an owner and an expected close — as a board you drag across or a table you page through, and the won and lost history behind it.',\n },\n designedEmails: {\n path: '/marketing-and-automation/email-campaigns/designed-emails',\n title: 'Designed emails',\n excerpt: 'Build campaign emails in the besigner with email-safe blocks and merge tokens — no separate editor.',\n },\n emailCampaigns: {\n path: '/marketing-and-automation/email-campaigns/overview',\n title: 'Email Campaigns',\n excerpt: 'Campaigns to audiences built from your contacts. Campaign email starts at Pro and needs a sending domain of the site\\'s own; receipts and account email send on every plan.',\n },\n events: {\n path: '/content-and-data/events/overview',\n title: 'Events Calendar',\n excerpt: 'Keep a schedule of events in the console and publish the ones you choose to any screen, with search-engine event markup.',\n },\n forms: {\n path: '/content-and-data/forms/overview',\n title: 'Forms & Lead Capture',\n excerpt: 'Add forms to your site, collect submissions in an inbox, and write them into datasets.',\n },\n installYourFirstPlugin: {\n path: '/guides/install-your-first-plugin',\n title: 'Install your first marketplace item',\n excerpt: 'A click-by-click walkthrough of the Marketplace — find something, choose which sites get it, install it, and turn it off again.',\n },\n inviteTeammates: {\n path: '/workspace-and-billing/teams-and-roles/invite-teammates',\n title: 'Invite teammates',\n excerpt: 'Add people to your site and understand how team members act within your organization.',\n },\n manifestAndEnvs: {\n path: '/developers/plugins/reference/manifest-and-envs',\n title: 'Manifests, trust lifecycle & environment',\n excerpt: 'The plugin manifest schema, the marketplace listing/version documents, the trust state machine, and every PLUGIN_* environment variable.',\n },\n marketingOverlays: {\n path: '/marketing-and-automation/marketing-overlays/overview',\n title: 'Marketing Overlays',\n excerpt: 'Site-wide announcement bars and promotional popups with triggers, scheduling, and email capture.',\n },\n membersOnly: {\n path: '/workspace-and-billing/teams-and-roles/members-only',\n title: 'Members-only areas',\n excerpt: 'Let visitors sign up as members and gate screens so only members can view them.',\n },\n orgAutomations: {\n path: '/marketing-and-automation/workflows-and-actions/org-automations',\n title: 'Org automations',\n excerpt: 'Write an automation once for the whole organization and run it on the sites you choose, with a pause on each site.',\n },\n plugins: {\n path: '/developers/plugins/overview',\n title: 'Plugins & Marketplace',\n excerpt: 'Extend Aglyn with sandboxed plugins — install from the marketplace, configure them, and publish your own.',\n },\n pos: {\n path: '/commerce-and-bookings/commerce/pos-and-reservations',\n title: 'POS & reservations',\n excerpt: 'Sell in person from the console register and take date-range reservations with deposits.',\n },\n publishAPlugin: {\n path: '/developers/plugins/publish-a-plugin',\n title: 'Publish a plugin',\n excerpt: 'Ship your own plugin to the marketplace with version pinning.',\n },\n publisherHandbook: {\n path: '/developers/plugins/publishing/publisher-handbook',\n title: 'Publisher handbook',\n excerpt: 'Publishing to the Aglyn marketplace — from profile setup through listing authoring, review, updates, and getting paid.',\n },\n redirects: {\n path: '/building-sites/redirects/overview',\n title: 'Redirects',\n excerpt: 'Manage URL redirects with validation, loop detection, and hit metrics.',\n },\n sandboxSecurity: {\n path: '/developers/plugins/reference/sandbox-security',\n title: 'Sandbox security model',\n excerpt: 'How sandboxed marketplace plugins are isolated — separate origin, per-manifest CSP, pinned artifacts — and what that means when you write one.',\n },\n sequences: {\n path: '/content-and-data/crm/sequences',\n title: 'Sequences',\n excerpt: 'One-to-one email sequences a rep sends to a person from their own connected mailbox, kept with the CRM. Rolling out.',\n },\n webhooks: {\n path: '/marketing-and-automation/workflows-and-actions/webhooks',\n title: 'Webhooks',\n excerpt: 'Connect Aglyn to other systems with outbound and inbound webhooks.',\n },\n} as const satisfies Record<string, PluginDocsTopic>\n\nexport type PluginDocsKey = keyof typeof PLUGIN_DOCS\n\nexport const PLUGIN_DOCS_ANCHORS = {\n abuseReports: ['#where-reports-come-from', '#triage-by-severity', '#csam', '#which-lever', '#statuses', '#disclosure', '#dmca', '#counter-notices', '#counter-notice-clock', '#counter-notice-steps', '#repeat-infringers', '#repeat-infringer-threshold', '#known-gaps', '#related'],\n actionsBuilder: ['#create-an-action', '#recipes', '#describe-it', '#triggers', '#crm-events', '#only-run-when-a-field-matches', '#chain-multiple-conditions-andor', '#steps', '#crm-steps', '#step-conditions', '#sequences', '#run-history', '#what-is-and-isnt-recorded', '#interactions-from-the-besigner', '#when-to-use-which', '#related'],\n aglynAssist: ['#what-it-can-do', '#aglyn-ai', '#answers-for-beginners-and-developers', '#offers-to-open-a-page', '#edits-in-the-besigner', '#where-an-answer-came-from', '#answers-straight-from-the-documentation', '#message-limits', '#feedback', '#privacy'],\n aiAutomations: ['#draft', '#explain', '#why-a-run-failed', '#what-is-sent', '#who-can-use-it', '#related'],\n aiCrm: ['#summarize-a-record', '#summaries-are-reused-until-the-record-changes', '#draft-an-email', '#match-columns', '#what-is-sent', '#who-can-use-it', '#related'],\n aiExperiments: ['#it-proposes-you-write', '#write-variants', '#putting-them-in', '#what-it-will-not-write', '#read-a-result', '#the-verdict', '#the-words', '#undecided', '#what-is-sent', '#who-can-use-it', '#related'],\n aiMonitoring: ['#the-ai-card', '#where-else', '#one-account', '#the-spend-leaderboard', '#alerts', '#related'],\n aiProducts: ['#write-a-products-copy', '#write-copy-for-many-products', '#when-you-import-products', '#propose-a-first-catalog', '#propose-categories-and-discounts', '#what-the-copy-never-says', '#what-is-sent-to-the-ai-provider', '#who-can-use-it', '#related'],\n aiSeo: ['#write-a-pages-listing', '#write-a-products-listing', '#audit-the-whole-site', '#target-keywords', '#apply-all-as-drafts', '#structured-data-and-llmstxt', '#related'],\n aiThemes: ['#change-what-you-describe-or-design-a-new-theme', '#match-your-brand', '#review-the-proposal', '#save-it-or-dont', '#who-can-use-it', '#related'],\n assistSignals: ['#the-workflow-this-board-exists-for', '#fleet', '#the-cache-read-rate-and-what-a-bad-number-looks-like', '#where-the-money-goes', '#tokens-by-kind', '#docs-gaps', '#questions-the-docs-could-not-answer', '#what-people-actually-asked', '#what-assist-costs-by-workspace', '#reading-the-sample-honestly', '#related'],\n billing: ['#tiers--entitlements', '#enterprise', '#single-sign-on-and-enforcement', '#usage-meters', '#who-is-generating-what', '#ai-allotments', '#storage-overage', '#if-you-would-rather-uploads-stopped', '#assist-overage', '#stop-ai-assist-at-the-included-band', '#ai-overage-ceiling', '#free-ai-credits', '#ai-credit-alerts', '#usage-budget', '#seats', '#crm-records', '#the-crm-suite', '#one-to-one-email', '#organization-data', '#api-access', '#payments', '#outstanding', '#plan-total', '#billing-email', '#payment-methods', '#billing-address', '#tax-ids', '#sales-tax', '#platform-fees', '#related'],\n bindings: ['#binding-tokens', '#rename-safe-id-tokens', '#insert-a-variable', '#token-pills', '#in-the-canvas-text-editor', '#site-details', '#typed-variables', '#no-code-functions', '#parameters-a-visitor-can-answer', '#a-calculator-you-lay-out', '#where-used--safety', '#workflows', '#related'],\n bookings: ['#set-up-bookings', '#taking-bookings', '#reminders', '#payments-and-fees', '#service-tax', '#manage', '#booking-from-the-crm', '#canceling-and-refunding', '#related'],\n buildAWorkflow: ['#1-open-the-workflows-page', '#2-choose-a-trigger', '#3-add-steps', '#waiting', '#4-save-and-test', '#duplicate-a-workflow', '#tips', '#related'],\n catalog: ['#products-options-and-variants', '#billing-modes-and-subscriptions', '#ai', '#categories-and-tags', '#collections', '#slugs', '#merchant-center-feed', '#related'],\n commerce: ['#products-hub', '#inventory', '#reserved-stock', '#stock-movements', '#gift-cards', '#recovery-and-alerts', '#orders', '#orders-screen', '#order-statuses', '#order-money-tiles', '#a-lost-dispute', '#shipping--taxes', '#lodging-tax-on-reservations', '#storefront-sales-tax', '#destination-coverage', '#dropshipping', '#related'],\n commerceEndToEnd: ['#1-connect-payments', '#2-create-products', '#3-design-the-storefront', '#catalog-search-filters-and-sort', '#category-pages', '#the-product-page-template', '#4-what-checkout-does', '#paying-without-leaving-your-site', '#5-run-orders-from-the-console', '#6-subscriptions--the-stripe-portal', '#related'],\n companies: ['#the-companies-list', '#create-a-company', '#a-companys-page', '#contacts-at-a-company', '#linked-on-capture', '#import-from-csv', '#export-csv', '#deleting-a-company', '#who-can-see-a-company', '#files', '#related'],\n consoleTour: ['#the-app-bar', '#in-context-help', '#primary-navigation', '#editing-vs-managing', '#the-sites-list', '#the-status-pill', '#how-the-pill-is-decided', '#your-site-allowance', '#a-sites-dashboard', '#next', '#workspace-settings--notifications', '#the-notifications-feed', '#notification-settings', '#one-kind-at-a-time', '#workspace-and-site-overrides', '#daily-digests', '#alerts-on-this-device'],\n contactActivities: ['#four-kinds-of-history', '#campaign-email', '#logging-an-activity', '#meeting-from-a-booking', '#click-to-call', '#sending-an-email', '#delivery-states', '#captured-email', '#where-an-activity-is-visible', '#the-recent-activity-feed', '#related'],\n contactFields: ['#define-a-field', '#fields-per-record', '#where-values-show', '#save-a-form-field', '#lead-source-values', '#over-the-api', '#retire-restore-delete', '#recompute-next-activity', '#related'],\n contactRecord: ['#adding-a-contact-by-hand', '#the-record-page', '#deleting-and-erasing', '#what-each-site-keeps-to-itself', '#merging-two-records', '#likely-duplicates', '#owner', '#last-engaged', '#lifecycle-stages', '#where-the-persons-lead-is', '#finding-a-contact', '#files', '#related'],\n contacts: ['#whats-in-the-crm-area', '#unified-ingestion', '#what-each-plan-includes', '#the-contacts-page', '#import-from-csv', '#segments', '#everywhere-the-crm-shows-up', '#capture-replies', '#at-the-organization-level', '#who-can-open-the-crm', '#related'],\n crmEmailTemplates: ['#templates-and-snippets', '#merge-fields', '#saving', '#managing-templates', '#duplicate-a-template', '#shared-or-personal', '#over-the-rest-api', '#related'],\n crmLeads: ['#what-makes-a-lead', '#what-a-lead-holds', '#adding-a-lead-by-hand', '#the-leads-list', '#working-a-lead-from-the-row', '#several-leads-at-once', '#export-csv', '#import-from-csv', '#who-owns-a-lead', '#a-leads-page', '#email-state', '#converting-a-lead', '#unqualifying-a-lead', '#erasing-the-person', '#who-can-do-this', '#related'],\n crmReports: ['#choosing-a-period', '#contacts', '#sources-and-lifecycle', '#conversion-by-source', '#lead-funnel', '#pipeline', '#forecast-by-close-month', '#won-and-lost', '#won-and-lost-by-owner', '#activity-by-teammate', '#tasks', '#exporting-a-table', '#crm-at-a-glance', '#how-the-numbers-are-counted', '#related'],\n crmSettings: ['#companies', '#create-companies-from-work-email-domains', '#default-owner', '#assignment-rules', '#round-robin', '#email-templates', '#email-capture', '#your-sending-addresses', '#recipes', '#related'],\n crmTasks: ['#the-tasks-page', '#the-calendar-view', '#snoozing-a-task', '#selecting-exporting-and-acting-on-many', '#import-from-csv', '#creating-a-task', '#assigning-a-task-to-someone-else', '#completing-and-reopening', '#organization-tasks', '#tasks-on-a-contact-company-or-deal', '#reminders', '#turning-reminders-off', '#next-activity', '#the-daily-digest', '#turning-it-off', '#the-dashboard-card', '#who-can-do-what', '#related'],\n crmViews: ['#the-views-control', '#a-view-is-a-link', '#filters', '#filters-on-the-contacts-list', '#filters-on-the-other-lists', '#columns-and-sort', '#segments-and-views', '#who-sees-what', '#related'],\n datasets: ['#model-builder', '#typed-documents', '#relations', '#query-layer', '#repeatable-components', '#who-a-dataset-is-shared-with', '#import--export', '#related'],\n deals: ['#pipelines', '#stages', '#the-board-and-the-table', '#import-from-csv', '#creating-a-deal', '#line-items', '#moving-winning-and-losing', '#a-won-deal-makes-its-contact-a-customer', '#a-deals-page', '#files', '#related'],\n designedEmails: ['#create-a-template', '#duplicate-a-template', '#styling-email-blocks', '#merge-tokens', '#send-it', '#the-plain-text-version', '#start-from-a-brief-instead'],\n emailCampaigns: ['#send-a-campaign', '#campaigns-belong-to-the-organization', '#organization-emails-page', '#campaigns-group-emails', '#what-belongs-to-a-campaign', '#who-the-email-comes-from', '#sending-domains', '#account-email-always-sends', '#marketing-needs-a-domain', '#two-ways-to-get-a-domain', '#a-domain-we-set-up-is-a-request', '#domain-states', '#senders', '#send-a-test', '#preview-the-email', '#monthly-send-cap', '#personalize-with-merge-tags', '#recipient-count', '#who-a-campaign-is-allowed-to-reach', '#schedule-a-send', '#duplicate-an-email', '#email-lists', '#manual-lists', '#list-members', '#add-to-a-list', '#import-a-list', '#remove-from-a-list', '#lists-built-from-a-rule', '#experiments', '#experiments-across-sites', '#opens--clicks', '#the-campaign-report', '#per-contact-engagement', '#which-links-were-clicked', '#revenue-from-a-campaign', '#conversions', '#compliance', '#list-unsubscribe', '#topics', '#preference-page', '#frequency-opt-down', '#double-opt-in', '#marketing-mail', '#frequency-cap', '#suppressions', '#add-a-suppression', '#platform-suppressions', '#related'],\n events: ['#manage-events', '#show-events-on-a-screen', '#search-engines', '#related'],\n forms: ['#reading-submissions-from-code', '#build-a-form', '#monthly-allowance-per-plan', '#spam-and-abuse-protection', '#the-per-site-monthly-ceiling', '#field-types', '#labels-and-placeholders', '#example-a-quick-survey', '#after-submit', '#example-grow-an-email-list-from-a-signup-form', '#where-submissions-go', '#the-inbox', '#who-a-submission-is-from', '#where-this-one-went', '#replying-to-a-submission', '#every-sites-inbox-at-once', '#one-forms-own-page', '#duplicate-a-form', '#switch-forms-off-for-one-site', '#related'],\n installYourFirstPlugin: ['#before-you-start', '#step-1-open', '#step-2-browse', '#step-3-reviews', '#step-4-targeting', '#step-5-install', '#step-6-use', '#step-7-off', '#what-to-do-next', '#related'],\n inviteTeammates: ['#invite-someone', '#pending-invites', '#who-gets-told', '#accepting-an-invite', '#an-invitation-never-changes-who-owns-the-workspace', '#how-team-members-act', '#you-are-a-site-collaborators-support-channel', '#help-a-teammate-who-is-locked-out', '#why-you-cant-always-set-a-password', '#activity-log', '#ai-actions', '#ai-usage', '#ai-allotment', '#tips', '#related'],\n manifestAndEnvs: ['#plugin-manifest-published-with-every-version', '#contributes--where-the-plugin-loads', '#config--settings-without-writing-a-settings-screen', '#listing--version-documents', '#review--trust-lifecycle', '#environment-variables', '#pluginsconfigjson-first-party-contributors'],\n marketingOverlays: ['#announcement-bar', '#promotional-popups', '#frequency', '#popup-v2', '#multiple-overlays-scheduling--page-targeting', '#variables-in-copy', '#engagement-stats', '#across-your-sites', '#related'],\n membersOnly: ['#let-visitors-sign-up', '#sign-in-sign-up-and-recovery-pages', '#forgotten-passwords', '#gate-a-screen', '#manage-your-members', '#suspend-or-reactivate-a-member', '#tips', '#related'],\n orgAutomations: ['#what-an-org-automation-is', '#create-one', '#triggers', '#steps', '#pause-it-on-one-site', '#waiting-switching-off-and-deleting', '#every-sites-own-automations', '#related'],\n plugins: ['#install--upgrade', '#browse-card', '#whats-included', '#what-the-badges-on-a-listing-mean', '#how-plugins-run', '#when-one-plugin-depends-on-another', '#a-dependency-that-is-off-for-one-site', '#configure', '#configure-site', '#publish-your-own', '#related'],\n pos: ['#registers', '#the-register', '#platform-fees-at-the-register', '#selling-past-the-count', '#when-something-disconnects', '#reservations', '#related'],\n publishAPlugin: ['#the-publish-pipeline', '#private-plugins', '#paid-listings', '#your-publisher-profile', '#tips', '#related'],\n publisherHandbook: ['#before-your-first-publish', '#the-publisher-agreement', '#where-to-publish-from', '#what-installing-each-type-does', '#rules-an-email-starter-has-to-meet', '#publishing-a-version', '#before-you-publish', '#review-what-happens-after-you-publish', '#the-two-badges-and-what-each-one-promises', '#asking-to-be-verified', '#testing-a-version-before-it-is-approved', '#watching-your-own-submission', '#disabled-versions', '#private-plugins', '#authoring-your-listing', '#what-your-listing-can-say-about-aglyn', '#versioning--updates', '#shipping-a-new-version', '#how-installs-work-the-buyer-side', '#getting-paid', '#low-prices-and-processing'],\n redirects: ['#manage-redirects', '#sending-visitors-to-another-site', '#metrics', '#match-modes-v2', '#related'],\n sandboxSecurity: ['#a-separate-origin', '#per-manifest-network-policy', '#when-you-cant-declare-the-origin', '#pinned-immutable-artifacts', '#what-this-means-when-you-build', '#related'],\n sequences: ['#what-it-is-for', '#sent-from-your-own-mailbox', '#where-it-lives', '#connect-a-mailbox', '#send-as', '#daily-cap', '#mailbox-status', '#auto-pause', '#mailbox-actions', '#link-domains', '#compliance-settings', '#allowed-countries', '#do-not-contact-domains', '#sequences', '#build-a-sequence', '#sequence-status', '#enroll', '#cold-contacts', '#enrollments', '#sending', '#what-stops-a-sequence', '#unsubscribe', '#related'],\n webhooks: ['#outbound-webhooks', '#inbound-webhooks', '#tips', '#related'],\n} as const satisfies Partial<Record<PluginDocsKey, readonly `#${string}`[]>>\n\ntype PluginAnchorMap = typeof PLUGIN_DOCS_ANCHORS\n\n/** Valid heading anchors for a plugin docs page (`never` when none). */\nexport type PluginDocsAnchor<K extends PluginDocsKey> =\n K extends keyof PluginAnchorMap ? PluginAnchorMap[K][number] : never\n"],"names":["PLUGIN_DOCS","abuseReports","path","title","excerpt","actionsBuilder","aglynAssist","aiAutomations","aiCrm","aiExperiments","aiMonitoring","aiProducts","aiSeo","aiThemes","assistSignals","billing","bindings","bookings","buildAWorkflow","catalog","commerce","commerceEndToEnd","companies","consoleTour","contactActivities","contactFields","contactRecord","contacts","crmEmailTemplates","crmLeads","crmReports","crmSettings","crmTasks","crmViews","datasets","deals","designedEmails","emailCampaigns","events","forms","installYourFirstPlugin","inviteTeammates","manifestAndEnvs","marketingOverlays","membersOnly","orgAutomations","plugins","pos","publishAPlugin","publisherHandbook","redirects","sandboxSecurity","sequences","webhooks","PLUGIN_DOCS_ANCHORS"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD,iDAAiD;AACjD,8CAA8C;AAC9C,oEAAoE;AAEpE,6EAA6E;AAC7E,2EAA2E;AAC3E,+EAA+E;AAW/E,OAAO,MAAMA,cAAc;IACzBC,cAAc;QACZC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAC,gBAAgB;QACdH,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAE,aAAa;QACXJ,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAG,eAAe;QACbL,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAI,OAAO;QACLN,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAK,eAAe;QACbP,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAM,cAAc;QACZR,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAO,YAAY;QACVT,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAQ,OAAO;QACLV,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAS,UAAU;QACRX,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAU,eAAe;QACbZ,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAW,SAAS;QACPb,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAY,UAAU;QACRd,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAa,UAAU;QACRf,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAc,gBAAgB;QACdhB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAe,SAAS;QACPjB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAgB,UAAU;QACRlB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAiB,kBAAkB;QAChBnB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAkB,WAAW;QACTpB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAmB,aAAa;QACXrB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAoB,mBAAmB;QACjBtB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAqB,eAAe;QACbvB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAsB,eAAe;QACbxB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAuB,UAAU;QACRzB,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAwB,mBAAmB;QACjB1B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAyB,UAAU;QACR3B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA0B,YAAY;QACV5B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA2B,aAAa;QACX7B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA4B,UAAU;QACR9B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA6B,UAAU;QACR/B,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA8B,UAAU;QACRhC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA+B,OAAO;QACLjC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAgC,gBAAgB;QACdlC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAiC,gBAAgB;QACdnC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAkC,QAAQ;QACNpC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAmC,OAAO;QACLrC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAoC,wBAAwB;QACtBtC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAqC,iBAAiB;QACfvC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAsC,iBAAiB;QACfxC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAuC,mBAAmB;QACjBzC,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAwC,aAAa;QACX1C,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAyC,gBAAgB;QACd3C,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA0C,SAAS;QACP5C,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA2C,KAAK;QACH7C,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA4C,gBAAgB;QACd9C,MAAM;QACNC,O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"}
|
|
@@ -3572,7 +3572,7 @@ export const ASSIST_DOCS_INDEX = [
|
|
|
3572
3572
|
title: 'Companies',
|
|
3573
3573
|
heading: 'The companies list',
|
|
3574
3574
|
anchor: '#the-companies-list',
|
|
3575
|
-
text: 'The list shows every company your site may see, newest activity first, with its domain, how many contacts are linked to it, its owner, when it was last changed and its next activity — when the earliest open task against it is due
|
|
3575
|
+
text: 'The list shows every company your site may see, newest activity first, with its domain, how many contacts are linked to it, its owner, when it was last changed and its next activity — when the earliest open task against it is due. The table\'s Search box, or Filters › Company, finds a company by the start of its name — the search reaches every company, not only the page on screen — Filters › Owner shows one teammate\'s accounts, and Next activity › is empty keeps only the companies with nothing scheduled. Clicking a row opens the company\'s page.\nThe Contacts column is a count kept on the company and moved with every link and unlink, so a page of companies costs no lookup per row. A company linked before the count existed can read lower than its page shows; the company\'s own page counts live, and is the figure to trust when the two differ.'
|
|
3576
3576
|
},
|
|
3577
3577
|
{
|
|
3578
3578
|
path: '/content-and-data/crm/companies',
|
|
@@ -3817,7 +3817,7 @@ export const ASSIST_DOCS_INDEX = [
|
|
|
3817
3817
|
title: 'Deals pipeline',
|
|
3818
3818
|
heading: 'The board and the table',
|
|
3819
3819
|
anchor: '#the-board-and-the-table',
|
|
3820
|
-
text: 'The section opens as a board of the chosen pipeline: one column per open stage, with Won and Lost folded away at the end until you expand them. Each card shows the deal\'s title, amount, the contact and company it is with, its owner, and how many days it has sat in its current stage. Drag a card between columns to move it, or use the card\'s menu to move it, mark it won, or mark it lost from the keyboard.\nAbove the board, three figures summarize what is open: the open count, the pipeline value (every open deal\'s amount, per currency) and the weighted value (each amount multiplied by its stage\'s probability).\nSwitch to the table for a paged list with the title, stage, amount, owner, expected close date, status and next activity of every deal in the pipeline, including the closed ones. The
|
|
3820
|
+
text: 'The section opens as a board of the chosen pipeline: one column per open stage, with Won and Lost folded away at the end until you expand them. Each card shows the deal\'s title, amount, the contact and company it is with, its owner, and how many days it has sat in its current stage. Drag a card between columns to move it, or use the card\'s menu to move it, mark it won, or mark it lost from the keyboard.\nAbove the board, three figures summarize what is open: the open count, the pipeline value (every open deal\'s amount, per currency) and the weighted value (each amount multiplied by its stage\'s probability).\nSwitch to the table for a paged list with the title, stage, amount, owner, expected close date, status and next activity of every deal in the pipeline, including the closed ones. The table\'s Filters panel narrows it by Status (open, won or lost), and Next activity › is empty keeps only the deals on the page with no open task scheduled against them — the ones the reports page counts as stuck.\nThe table\'s rows have checkboxes: tick some and a bulk bar appears above it to set their stage, set their owner, mark them lost with one reason, export them or delete them. Export CSV above the table downloads the deals on screen as deals.csv — title, pipeline and stage by name, the amount in major units beside its currency, the owner by email address, the expected close date, status, the contact and the company, when it closed, the lost reason and notes; the bar\'s Export CSV writes the same file over the selection.'
|
|
3821
3821
|
},
|
|
3822
3822
|
{
|
|
3823
3823
|
path: '/content-and-data/crm/deals',
|
|
@@ -3985,7 +3985,7 @@ export const ASSIST_DOCS_INDEX = [
|
|
|
3985
3985
|
title: 'Leads',
|
|
3986
3986
|
heading: 'The Leads list',
|
|
3987
3987
|
anchor: '#the-leads-list',
|
|
3988
|
-
text: 'The list shows the most recently seen leads first — a person who booked yesterday sits above one who signed up last month, whichever came first. Each row carries:\n| Column | What it shows |\n| Lead | The name the person gave, with their email beneath it — or the email alone. |\n| Company, Title | The lead\'s own company and job title, as text. |\n| Status | New, Working, Qualified or Unqualified. Change it in place from the row. |\n| Email | The last verdict on the lead\'s address — see Email state — or a dash when nothing is known. |\n| Owner | The team member working the lead, or Unassigned. A lead inherits its contact\'s owner when one is assigned on capture. |\n| Source | Every surface that captured this person: Booking, the form they submitted, an import, New lead, or the API — and Sign-up on leads filed before sign-ups stopped making leads. |\n| Lead source | The lead\'s lead source. Sorting by it follows the order of your list, not the alphabet. |\n| Tags | The lead\'s tags. |\n| Campaign | The campaigns the lead is filed under, by name. Under a site only — a campaign belongs to one site. |\n| Last seen | When the person last did something on your site. |\nThe
|
|
3988
|
+
text: 'The list shows the most recently seen leads first — a person who booked yesterday sits above one who signed up last month, whichever came first. Each row carries:\n| Column | What it shows |\n| Lead | The name the person gave, with their email beneath it — or the email alone. |\n| Company, Title | The lead\'s own company and job title, as text. |\n| Status | New, Working, Qualified or Unqualified. Change it in place from the row. |\n| Email | The last verdict on the lead\'s address — see Email state — or a dash when nothing is known. |\n| Owner | The team member working the lead, or Unassigned. A lead inherits its contact\'s owner when one is assigned on capture. |\n| Source | Every surface that captured this person: Booking, the form they submitted, an import, New lead, or the API — and Sign-up on leads filed before sign-ups stopped making leads. |\n| Lead source | The lead\'s lead source. Sorting by it follows the order of your list, not the alphabet. |\n| Tags | The lead\'s tags. |\n| Campaign | The campaigns the lead is filed under, by name. Under a site only — a campaign belongs to one site. |\n| Last seen | When the person last did something on your site. |\nThe table\'s own toolbar filters the list: Filters opens the filter panel, and Search finds a lead by name, email, company, title or tag. Status, Email, Owner, Lead source and Campaign are picked from a list in the panel, and from each column\'s menu. Status starts on Open (new or working), every lead that still needs working; pick one status instead, or remove the filter to see every lead. A lead nobody has touched yet has no status of its own and reads as New, so the leads your site collected before the CRM existed are already in the Open view. Email narrows by the address\'s verdict — Cannot be emailed for every lead a sender '
|
|
3989
3989
|
},
|
|
3990
3990
|
{
|
|
3991
3991
|
path: '/content-and-data/crm/leads',
|
|
@@ -4160,7 +4160,7 @@ export const ASSIST_DOCS_INDEX = [
|
|
|
4160
4160
|
title: 'Reports',
|
|
4161
4161
|
heading: 'Pipeline',
|
|
4162
4162
|
anchor: '#pipeline',
|
|
4163
|
-
text: '- Open deals and their pipeline value — the face value of every open deal, counted on the server.\n- Weighted forecast — each open deal at the odds of its stage (a deal in a 60% stage counts 60% of its amount). Won and lost deals are never in the pipeline: a won deal is revenue and a lost one is history.\n- Deals by stage, per pipeline, each stage with its count, value and weighted value. A deal sitting in a stage the pipeline no longer has, or in no pipeline at all, is listed separately and is worth nothing to the forecast.\n- Top open deals — the ten largest, each a link to the deal.\n- Stuck deals — the open deals with no next activity: nothing scheduled against them. Painted as a warning above zero and a link to the deals list,
|
|
4163
|
+
text: '- Open deals and their pipeline value — the face value of every open deal, counted on the server.\n- Weighted forecast — each open deal at the odds of its stage (a deal in a 60% stage counts 60% of its amount). Won and lost deals are never in the pipeline: a won deal is revenue and a lost one is history.\n- Deals by stage, per pipeline, each stage with its count, value and weighted value. A deal sitting in a stage the pipeline no longer has, or in no pipeline at all, is listed separately and is worth nothing to the forecast.\n- Top open deals — the ten largest, each a link to the deal.\n- Stuck deals — the open deals with no next activity: nothing scheduled against them. Painted as a warning above zero and a link to the deals list, where Next activity › is empty lists them.\nDeals in more than one currency are added as numbers and shown in the most common currency, with a note saying so.'
|
|
4164
4164
|
},
|
|
4165
4165
|
{
|
|
4166
4166
|
path: '/content-and-data/crm/reports',
|
|
@@ -4398,7 +4398,7 @@ export const ASSIST_DOCS_INDEX = [
|
|
|
4398
4398
|
title: 'Tasks & follow-ups',
|
|
4399
4399
|
heading: 'The tasks page',
|
|
4400
4400
|
anchor: '#the-tasks-page',
|
|
4401
|
-
text: 'The Tasks section is one list with six views, chosen
|
|
4401
|
+
text: 'The Tasks section is one list with six views, chosen under Show in the table\'s Filters panel — My tasks unless you choose another:\n| View | What it shows |\n| My tasks | Open tasks assigned to you, soonest due first. Tasks with no due date come last. |\n| Overdue | Open tasks whose due date is before today. |\n| Today | Open tasks due at any time today. |\n| Upcoming | Open tasks due tomorrow or later. |\n| All open | Every open task, whoever it is assigned to. |\n| Done | Completed tasks, most recently due first. |\n"Overdue" and "today" are not stored on the task — nothing runs at midnight to stamp them. Each view is a window over the due date computed from your own clock and time zone when you look, and a tab left open across midnight repaints yesterday\'s work as overdue on its own.\nEach row shows a checkbox that completes or reopens the task, the title (with the first line of notes under it), the kind, the priority, the due date colored by where it stands — red when overdue, amber when due today — with a snooze beside it, the assignee, and the record it is for, as a link into that record\'s page. A view shows at most 200 rows and says so when it is full; narrow the view to see the rest.\nThe calendar view\nList and Calendar sit beside the view control. The calendar draws one month, placing each task on the day it is due, and clicking one opens the same drawer a row does. The arrows page a month at a time and Today comes back.\nThe calendar draws the same tasks the list is showing — the same view, the same window, and no extra reading — so a month that looks empty may simply be a month the view\'s tasks are not in. It says so underneath: how many of them are due outside the month on screen, how many have no due date at all (a calendar cannot place those), and whether the view is'
|
|
4402
4402
|
},
|
|
4403
4403
|
{
|
|
4404
4404
|
path: '/content-and-data/crm/tasks',
|
|
@@ -4426,7 +4426,7 @@ export const ASSIST_DOCS_INDEX = [
|
|
|
4426
4426
|
title: 'Tasks & follow-ups',
|
|
4427
4427
|
heading: 'Next activity',
|
|
4428
4428
|
anchor: '#next-activity',
|
|
4429
|
-
text: 'Every contact, company and deal carries a Next activity: when its earliest open task is due. It is a column on the contacts, companies and deals lists — overdue in the warning color, today emphasized, a dash when nothing is scheduled — and
|
|
4429
|
+
text: 'Every contact, company and deal carries a Next activity: when its earliest open task is due. It is a column on the contacts, companies and deals lists — overdue in the warning color, today emphasized, a dash when nothing is scheduled — and filtering it by is empty in the table\'s Filters panel keeps only the records with nothing planned. A view saved with that filter reopens with it. On the reports page, the pipeline card\'s Stuck deals counts the open deals with nothing scheduled.\nThe figure is kept by every task write: creating, editing, snoozing, completing, reopening or deleting a task recomputes it for the records the task names, and so does a task filed by an automation or over the API. A record that existed before the figure did shows a dash until a task against it is written — or until Recompute next activity in the Fields section rewrites every record\'s figure from its open tasks.'
|
|
4430
4430
|
},
|
|
4431
4431
|
{
|
|
4432
4432
|
path: '/content-and-data/crm/tasks',
|
|
@@ -4468,7 +4468,7 @@ export const ASSIST_DOCS_INDEX = [
|
|
|
4468
4468
|
title: 'Saved views',
|
|
4469
4469
|
heading: 'The views control',
|
|
4470
4470
|
anchor: '#the-views-control',
|
|
4471
|
-
text: 'Above each list, a button names the view the list is showing — All contacts when none is — and opens the views menu:\n- My views are the ones you saved for yourself.\n- Shared with the team lists views a colleague saved and shared.\n- On the Contacts list, your saved segments are offered under their own heading; choosing one puts its tags and sources
|
|
4471
|
+
text: 'Above each list, a button names the view the list is showing — All contacts when none is — and opens the views menu:\n- My views are the ones you saved for yourself.\n- Shared with the team lists views a colleague saved and shared.\n- On the Contacts list, your saved segments are offered under their own heading; choosing one puts its tags and sources among the list\'s filters.\nBeneath them are the acts on the open view:\n| Action | What happens |\n| Save changes | The filters, columns and sort as they are now become the view\'s. Offered on a view you may change — one you made, or any view when you are an organization-wide member. |\n| Save as view… / Save as new view… | Name the current arrangement and keep it. Tick Share with the team to list it for everyone who can open the section. |\n| Rename… | Change the name. |\n| Share with the team / Stop sharing | List the view for the whole team, or take it back to yours alone. |\n| Set as default / Clear default | Open the section on this view whenever you come to it. The default is yours: a colleague\'s default is theirs. |\n| Discard changes / Clear filters | Back to the view\'s saved arrangement, or to the plain list. |\n| Save as segment… | Contacts only — see Segments and views. |\n| Delete view… | After a confirmation. The records themselves are untouched. |\nA view you have changed is marked Modified beside its name, so a list that reads "My open leads" and shows something else cannot pass for the saved one.'
|
|
4472
4472
|
},
|
|
4473
4473
|
{
|
|
4474
4474
|
path: '/content-and-data/crm/views',
|
|
@@ -4477,12 +4477,26 @@ export const ASSIST_DOCS_INDEX = [
|
|
|
4477
4477
|
anchor: '#a-view-is-a-link',
|
|
4478
4478
|
text: 'The address of a section carries the open view as ?view=…. Copy the address and a colleague who can open the section lands on the same list. On the Contacts list the key sits beside the ones a form\'s page or the Inbox link with, so "the people this form captured, in my usual view" is one address.'
|
|
4479
4479
|
},
|
|
4480
|
+
{
|
|
4481
|
+
path: '/content-and-data/crm/views',
|
|
4482
|
+
title: 'Saved views',
|
|
4483
|
+
heading: 'Filters',
|
|
4484
|
+
anchor: '#filters',
|
|
4485
|
+
text: 'Every CRM list filters through its table\'s own toolbar: Filters opens the filter panel, Search finds a record by its name and the words beside it, and each column\'s menu has Filter too. Pick a column, a condition and a value; a field with fixed choices — a status, a stage, an owner, a lead source, a campaign, a kind, a priority — is picked from a list, a date from a calendar, anything else typed.\nThe panel edits one filter at a time. Filters on different columns add up: filter one column, then another, and the list keeps both. Every filter in force shows as a chip beside the views control, where its ✕ removes it, and all of them are part of the saved view. A view saved before the panel existed keeps its filters as they were.'
|
|
4486
|
+
},
|
|
4480
4487
|
{
|
|
4481
4488
|
path: '/content-and-data/crm/views',
|
|
4482
4489
|
title: 'Saved views',
|
|
4483
4490
|
heading: 'Filters on the Contacts list',
|
|
4484
4491
|
anchor: '#filters-on-the-contacts-list',
|
|
4485
|
-
text: '
|
|
4492
|
+
text: 'On the Contacts list the fields are the list\'s columns and a few that are not — owner, stage, source, company, tags, form, orders, lifetime value, created and updated dates, email verdict, next activity, and one field per custom field your organization has defined.\nOne chip reaches every contact and the rest narrow what it found. The database answers one filter per query, so the first filter it can serve — a name, an email, a tag, a form, a date, an order count — is run against the whole collection, and every other filter narrows the newest thousand contacts that query returned. The served chip is filled; the others are outlined; and the caption under the chips says which is which. Owner, stage, source, company and custom-field filters always narrow rather than reach, because those facts are kept per site and no query can reach them directly. Sorting reorders the loaded window.'
|
|
4493
|
+
},
|
|
4494
|
+
{
|
|
4495
|
+
path: '/content-and-data/crm/views',
|
|
4496
|
+
title: 'Saved views',
|
|
4497
|
+
heading: 'Filters on the other lists',
|
|
4498
|
+
anchor: '#filters-on-the-other-lists',
|
|
4499
|
+
text: '- Leads — Status, Email, Owner, Lead source and Campaign; see the leads list. The list reads the 200 most recently seen leads, and every filter and the search narrow those.\n- Companies — Company (starts with or is) and Owner reach every company; the search box is the same Company starts with filter. The list is paged, so one of the two applies at a time.\n- Deals (the table) — Status reaches every deal. There is no search box: nothing indexes a deal\'s title, and a search over one page would miss the deal on the next.\n- Tasks — Show is the task view (My tasks, Overdue, Today, Upcoming, All open, Done) and reaches every task; Kind, Priority and Assignee, and the search over title and notes, narrow the rows that view loaded.\nOn the contacts, companies and deals lists, Next activity › is empty keeps only the records with no open task scheduled against them (see next activity). It narrows the rows already loaded rather than the whole collection, the way a contacts filter that cannot be served does, and it stays beside the companies and deals lists\' one served filter.'
|
|
4486
4500
|
},
|
|
4487
4501
|
{
|
|
4488
4502
|
path: '/content-and-data/crm/views',
|
|
@@ -4496,7 +4510,7 @@ export const ASSIST_DOCS_INDEX = [
|
|
|
4496
4510
|
title: 'Saved views',
|
|
4497
4511
|
heading: 'Segments and views',
|
|
4498
4512
|
anchor: '#segments-and-views',
|
|
4499
|
-
text: 'A segment is the older, narrower thing: saved tags and sources, usable as a campaign audience. Views keep everything a segment kept and more, and the two meet in two places:\n- Every segment is offered in the Contacts views menu. Choosing one puts its tags and sources
|
|
4513
|
+
text: 'A segment is the older, narrower thing: saved tags and sources, usable as a campaign audience. Views keep everything a segment kept and more, and the two meet in two places:\n- Every segment is offered in the Contacts views menu. Choosing one puts its tags and sources among the list\'s filters, where you can narrow further and save the result as a view.\n- Save as segment… in the views menu keeps the tag and source filters of the current view as a segment. The other filters stay on the view.\nA contacts view can also be an email audience in its own right. When you build an audience from a rule, pick it under Saved view, beside Saved segment: the view\'s filters — owner, stage, company, tags, sources, dates, purchases and custom fields — always apply, the way a segment\'s do. Only views whose filters can be an audience are offered: a view filtered by a name or an email, or by the updated date, describes a list on a screen rather than a set of people, and is left out. A view that is deleted, or edited past what an audience can express after a rule named it, makes that audience select nobody rather than everybody.'
|
|
4500
4514
|
},
|
|
4501
4515
|
{
|
|
4502
4516
|
path: '/content-and-data/crm/views',
|
|
@@ -5413,7 +5427,7 @@ export const ASSIST_DOCS_INDEX = [
|
|
|
5413
5427
|
title: 'Extension-point catalog',
|
|
5414
5428
|
heading: '',
|
|
5415
5429
|
anchor: '',
|
|
5416
|
-
text: 'Extension-point catalog\nThe surface matrix: what a plugin can extend, from which entry (barrel = client, /server = API process), and when it runs.\n| Extension point | Entry | Reaches | Runs |\n| Canvas components (defineUiFeatureBundle) | barrel (site) | Besigner + published sites | Editor gates / SSR-suspended page load |\n| Console nav + pages (ConsoleExtension.navItems) | barrel (console) | Console host area | After the org resolves, before the shell paints |\n| Widgets (ConsoleExtension.widgets) | barrel (console) | Named console zones (dashboard, org, billing, team, staff, besigner, the assistant dock) — a widget with a column is a column of a shell-owned table | With their host page |\n| Providers (ConsoleExtension.providers) | barrel (console) | Around every console page | Once the registry is populated |\n| Zones a plugin HOSTS (definePluginZone / registerPluginZone) | barrel (console) | A position on a page the plugin owns, with the props it hands each widget carried on the token — so another plugin writes a widget for it without importing the owner | With the page that draws useConsoleWidgetSlot(); the token itself is type-only and ships nothing |\n| Staff pages (ConsoleExtension.staffPages) | barrel (staff) | The staff area — a tab in the staff strip and a page at /admin/{id} | Once the staff area has loaded its plugins, behind the staff guard |\n| Site runtimes (registerSiteRuntime) | barrel (site) | Every published page | Client render, reading enricher props |\n| Redirect resolvers / page resolvers / enrichers | /server | Tenant page pipeline | Per request, in that order; enricher errors isolated |\n| API routes (registerPluginApiRoute) | /server
|
|
5430
|
+
text: 'Extension-point catalog\nThe surface matrix: what a plugin can extend, from which entry (barrel = client, /server = API process), and when it runs.\n| Extension point | Entry | Reaches | Runs |\n| Canvas components (defineUiFeatureBundle) | barrel (site) | Besigner + published sites | Editor gates / SSR-suspended page load |\n| Console nav + pages (ConsoleExtension.navItems) | barrel (console) | Console host area | After the org resolves, before the shell paints |\n| Widgets (ConsoleExtension.widgets) | barrel (console) | Named console zones (dashboard, org, billing, team, staff, besigner, the assistant dock) — a widget with a column is a column of a shell-owned table | With their host page |\n| Providers (ConsoleExtension.providers) | barrel (console) | Around every console page | Once the registry is populated |\n| Zones a plugin HOSTS (definePluginZone / registerPluginZone) | barrel (console) | A position on a page the plugin owns, with the props it hands each widget carried on the token — so another plugin writes a widget for it without importing the owner | With the page that draws useConsoleWidgetSlot(); the token itself is type-only and ships nothing |\n| Staff pages (ConsoleExtension.staffPages) | barrel (staff) | The staff area — a tab in the staff strip and a page at /admin/{id} | Once the staff area has loaded its plugins, behind the staff guard |\n| Site runtimes (registerSiteRuntime) | barrel (site) | Every published page | Client render, reading enricher props |\n| Redirect resolvers / page resolvers / enrichers | /server | Tenant page pipeline | Per request, in that order; enricher errors isolated |\n| API routes (registerPluginApiRoute) | /server (tenantApi / consoleApi) | /api/ of the app whose surface registered the route: tenantApi routes answer on the published'
|
|
5417
5431
|
},
|
|
5418
5432
|
{
|
|
5419
5433
|
path: '/developers/plugins/reference/injection-zones',
|