@aglyn/aglyn 1.0.0-beta.182 → 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.
Files changed (36) hide show
  1. package/package.json +11 -11
  2. package/src/lib/app-utils/compose-reusable-components.d.ts +10 -0
  3. package/src/lib/app-utils/compose-reusable-components.js +6 -1
  4. package/src/lib/app-utils/compose-reusable-components.js.map +1 -1
  5. package/src/lib/app-utils/consent-groups.d.ts +23 -1
  6. package/src/lib/app-utils/consent-groups.js +29 -1
  7. package/src/lib/app-utils/consent-groups.js.map +1 -1
  8. package/src/lib/app-utils/definition-canvas-tree.d.ts +24 -0
  9. package/src/lib/app-utils/definition-canvas-tree.js +38 -0
  10. package/src/lib/app-utils/definition-canvas-tree.js.map +1 -1
  11. package/src/lib/app-utils/docs-help.generated.d.ts +1 -1
  12. package/src/lib/app-utils/docs-help.generated.js +2 -0
  13. package/src/lib/app-utils/docs-help.generated.js.map +1 -1
  14. package/src/lib/app-utils/docs-index.generated.js +33 -12
  15. package/src/lib/app-utils/docs-index.generated.js.map +1 -1
  16. package/src/lib/app-utils/load-referenced-components.d.ts +166 -0
  17. package/src/lib/app-utils/load-referenced-components.js +219 -0
  18. package/src/lib/app-utils/load-referenced-components.js.map +1 -0
  19. package/src/lib/app-utils/reusable-component-kind.d.ts +119 -0
  20. package/src/lib/app-utils/reusable-component-kind.js +119 -0
  21. package/src/lib/app-utils/reusable-component-kind.js.map +1 -0
  22. package/src/lib/app-utils/server.d.ts +1 -0
  23. package/src/lib/app-utils/server.js +2 -0
  24. package/src/lib/app-utils/server.js.map +1 -1
  25. package/src/lib/foundation/constants/components.d.ts +7 -0
  26. package/src/lib/foundation/constants/components.js +6 -0
  27. package/src/lib/foundation/constants/components.js.map +1 -1
  28. package/src/lib/foundation/definitions/platform.types.d.ts +29 -0
  29. package/src/lib/foundation/definitions/platform.types.js.map +1 -1
  30. package/src/lib/foundation/index.d.ts +1 -1
  31. package/src/lib/foundation/index.js.map +1 -1
  32. package/src/lib/plugin-manager/first-party-plugins.generated.js +6 -0
  33. package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
  34. package/src/lib/plugin-manager/plugin-resource-drafts.d.ts +17 -3
  35. package/src/lib/plugin-manager/plugin-resource-drafts.js +2 -2
  36. package/src/lib/plugin-manager/plugin-resource-drafts.js.map +1 -1
@@ -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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"}
@@ -58,4 +58,28 @@ export declare function canvasTreeToDefinition(nodes: Nodes | undefined): {
58
58
  nodes: Nodes;
59
59
  ambiguousRoot: boolean;
60
60
  };
61
+ /** One element of a nested tree, as an element preset's `data` holds it. */
62
+ interface NestedNode {
63
+ $id?: string | null;
64
+ nodes?: NestedNode[];
65
+ [key: string]: unknown;
66
+ }
67
+ /**
68
+ * A nested tree — an element preset's `data` — as a definition: a node map
69
+ * keyed by fresh ids, with `rootId` naming the outermost element (AGL-3287).
70
+ *
71
+ * What a new component is seeded with when it starts from a preset, such as
72
+ * a Header email block starting from the email plugin's Header. The canvas
73
+ * mints ids the same way when the preset is dropped into a document, so a
74
+ * component started from a preset and one promoted from a dropped copy of it
75
+ * hold the same tree.
76
+ *
77
+ * Values are copied, never shared: a registered preset is a live object the
78
+ * drawer keeps reading, and a definition that aliased its props would carry
79
+ * every later edit back into the preset.
80
+ */
81
+ export declare function nestedToDefinition(nested: NestedNode, createId: () => string): {
82
+ rootId: string;
83
+ nodes: Nodes;
84
+ };
61
85
  export {};
@@ -1,4 +1,5 @@
1
1
  import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
2
3
  /**
3
4
  * @license
4
5
  * Copyright 2026 Aglyn LLC
@@ -122,5 +123,42 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
122
123
  ambiguousRoot: false
123
124
  };
124
125
  }
126
+ /**
127
+ * A nested tree — an element preset's `data` — as a definition: a node map
128
+ * keyed by fresh ids, with `rootId` naming the outermost element (AGL-3287).
129
+ *
130
+ * What a new component is seeded with when it starts from a preset, such as
131
+ * a Header email block starting from the email plugin's Header. The canvas
132
+ * mints ids the same way when the preset is dropped into a document, so a
133
+ * component started from a preset and one promoted from a dropped copy of it
134
+ * hold the same tree.
135
+ *
136
+ * Values are copied, never shared: a registered preset is a live object the
137
+ * drawer keeps reading, and a definition that aliased its props would carry
138
+ * every later edit back into the preset.
139
+ */ export function nestedToDefinition(nested, createId) {
140
+ const nodes = {};
141
+ const visit = (node, parentId)=>{
142
+ const { $id: _presetId, nodes: children } = node, fields = _object_without_properties_loose(node, [
143
+ "$id",
144
+ "nodes"
145
+ ]);
146
+ const id = createId();
147
+ const entry = _extends({}, JSON.parse(JSON.stringify(fields)), {
148
+ $id: id,
149
+ parentId
150
+ });
151
+ nodes[id] = entry;
152
+ if (Array.isArray(children) && children.length) {
153
+ entry.nodes = children.map((child)=>visit(child, id));
154
+ }
155
+ return id;
156
+ };
157
+ const rootId = visit(nested != null ? nested : {}, null);
158
+ return {
159
+ rootId,
160
+ nodes
161
+ };
162
+ }
125
163
 
126
164
  //# sourceMappingURL=definition-canvas-tree.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/definition-canvas-tree.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CANVAS_ROOT_ELEMENT_ID } from '../foundation/constants/canvas'\n\ntype Nodes = Record<string, any>\n\n/**\n * Adapt a reusable-component definition for the canvas, and back (AGL-680).\n *\n * The canvas requires its root to be the canonical `_@_` node — `setNodes`\n * stores whatever it is handed and `rootNode` looks up that exact id, so a\n * tree without it has no root and renders nothing.\n *\n * A definition's root is NOT `_@_`: `promoteToComponent` walks a selected\n * subtree, so the root is whichever node was selected. Loading one straight\n * into the canvas therefore produced an empty editor with \"Error rendering\n * undefined\" in the console — found by the editor smoke test, not by\n * typechecking, because both shapes are `Record<string, node>`.\n *\n * So the definition is wrapped under a synthetic canvas root on the way in\n * and unwrapped on the way out. The wrapper is never published: the tenant\n * runtime grafts from `rootId`, and an extra always-empty container in\n * every instance would be both wrong and wasteful.\n *\n * A map with no findable root always yields the empty canvas root rather\n * than being passed through (AGL-753). Passing it through renders `'Invalid\n * node'` with Add Element disabled — an editor with no way out, so the\n * document can never be repaired from the UI. Components created before\n * AGL-693 are stored as exactly that (`nodes: {}`), which is why this is a\n * load-time repair and not a migration.\n */\nexport function definitionToCanvasTree(definition: {\n rootId?: string\n nodes?: Nodes\n}): Nodes {\n const nodes = definition?.nodes ?? {}\n // Already canvas-shaped (a tree saved by this editor) — leave it alone.\n if (nodes[CANVAS_ROOT_ELEMENT_ID]) return nodes\n\n const rootId =\n definition?.rootId && nodes[definition.rootId]\n ? definition.rootId\n : (Object.keys(nodes).find((id) => !nodes[id]?.parentId) ??\n // A definition promoted out of a screen can keep the `parentId` it\n // had there, so \"no parent\" is not always \"no parentId\" — a parent\n // that is not itself in the map is just as much a root.\n Object.keys(nodes).find((id) => !nodes[nodes[id]?.parentId]))\n if (!rootId) {\n return {\n [CANVAS_ROOT_ELEMENT_ID]: {\n $id: CANVAS_ROOT_ELEMENT_ID,\n componentId: 'div',\n parentId: null,\n nodes: [],\n },\n }\n }\n\n return {\n ...nodes,\n [rootId]: { ...nodes[rootId], parentId: CANVAS_ROOT_ELEMENT_ID },\n [CANVAS_ROOT_ELEMENT_ID]: {\n $id: CANVAS_ROOT_ELEMENT_ID,\n componentId: 'box',\n parentId: null,\n nodes: [rootId],\n },\n }\n}\n\n/**\n * Recover the definition shape from a canvas tree.\n *\n * Returns the single child of the synthetic root as `rootId` and drops the\n * wrapper. A canvas root with several children means the author added\n * siblings at the top level — there is no single definition root to name,\n * so the wrapper is kept and reported, letting the caller refuse rather\n * than silently publishing only the first child.\n */\nexport function canvasTreeToDefinition(nodes: Nodes | undefined): {\n rootId?: string\n nodes: Nodes\n ambiguousRoot: boolean\n} {\n const map = nodes ?? {}\n const canvasRoot = map[CANVAS_ROOT_ELEMENT_ID]\n if (!canvasRoot) {\n const rootId = Object.keys(map).find((id) => !map[id]?.parentId)\n return { rootId, nodes: map, ambiguousRoot: false }\n }\n\n const children: string[] = Array.isArray(canvasRoot.nodes)\n ? canvasRoot.nodes.filter((id: string) => map[id])\n : []\n if (children.length !== 1) {\n return {\n rootId: CANVAS_ROOT_ELEMENT_ID,\n nodes: map,\n ambiguousRoot: true,\n }\n }\n\n const rootId = children[0]\n const stripped: Nodes = {}\n for (const [id, node] of Object.entries(map)) {\n if (id === CANVAS_ROOT_ELEMENT_ID) continue\n stripped[id] = id === rootId ? { ...node, parentId: null } : node\n }\n return { rootId, nodes: stripped, ambiguousRoot: false }\n}\n"],"names":["CANVAS_ROOT_ELEMENT_ID","definitionToCanvasTree","definition","Object","nodes","rootId","keys","find","id","parentId","$id","componentId","canvasTreeToDefinition","map","canvasRoot","ambiguousRoot","children","Array","isArray","filter","length","stripped","node","entries"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,sBAAsB,QAAQ,oCAAgC;AAIvE;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASC,uBAAuBC,UAGtC;cAQQC;IAPP,MAAMC,gBAAQF,8BAAAA,WAAYE,KAAK,mBAAI,CAAC;IACpC,wEAAwE;IACxE,IAAIA,KAAK,CAACJ,uBAAuB,EAAE,OAAOI;IAE1C,MAAMC,SACJH,CAAAA,8BAAAA,WAAYG,MAAM,KAAID,KAAK,CAACF,WAAWG,MAAM,CAAC,GAC1CH,WAAWG,MAAM,IAChBF,oBAAAA,OAAOG,IAAI,CAACF,OAAOG,IAAI,CAAC,CAACC;YAAQJ;eAAD,GAACA,YAAAA,KAAK,CAACI,GAAG,qBAATJ,UAAWK,QAAQ;kBAApDN,oBACD,mEAAmE;IACnE,mEAAmE;IACnE,wDAAwD;IACxDA,OAAOG,IAAI,CAACF,OAAOG,IAAI,CAAC,CAACC;YAAcJ;eAAP,CAACA,KAAK,EAACA,YAAAA,KAAK,CAACI,GAAG,qBAATJ,UAAWK,QAAQ,CAAC;;IACjE,IAAI,CAACJ,QAAQ;QACX,OAAO;YACL,CAACL,uBAAuB,EAAE;gBACxBU,KAAKV;gBACLW,aAAa;gBACbF,UAAU;gBACVL,OAAO,EAAE;YACX;QACF;IACF;IAEA,OAAO,aACFA;QACH,CAACC,OAAO,EAAE,aAAKD,KAAK,CAACC,OAAO;YAAEI,UAAUT;;QACxC,CAACA,uBAAuB,EAAE;YACxBU,KAAKV;YACLW,aAAa;YACbF,UAAU;YACVL,OAAO;gBAACC;aAAO;QACjB;;AAEJ;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASO,uBAAuBR,KAAwB;IAK7D,MAAMS,MAAMT,gBAAAA,QAAS,CAAC;IACtB,MAAMU,aAAaD,GAAG,CAACb,uBAAuB;IAC9C,IAAI,CAACc,YAAY;QACf,MAAMT,SAASF,OAAOG,IAAI,CAACO,KAAKN,IAAI,CAAC,CAACC;gBAAQK;mBAAD,GAACA,UAAAA,GAAG,CAACL,GAAG,qBAAPK,QAASJ,QAAQ;;QAC/D,OAAO;YAAEJ;YAAQD,OAAOS;YAAKE,eAAe;QAAM;IACpD;IAEA,MAAMC,WAAqBC,MAAMC,OAAO,CAACJ,WAAWV,KAAK,IACrDU,WAAWV,KAAK,CAACe,MAAM,CAAC,CAACX,KAAeK,GAAG,CAACL,GAAG,IAC/C,EAAE;IACN,IAAIQ,SAASI,MAAM,KAAK,GAAG;QACzB,OAAO;YACLf,QAAQL;YACRI,OAAOS;YACPE,eAAe;QACjB;IACF;IAEA,MAAMV,SAASW,QAAQ,CAAC,EAAE;IAC1B,MAAMK,WAAkB,CAAC;IACzB,KAAK,MAAM,CAACb,IAAIc,KAAK,IAAInB,OAAOoB,OAAO,CAACV,KAAM;QAC5C,IAAIL,OAAOR,wBAAwB;QACnCqB,QAAQ,CAACb,GAAG,GAAGA,OAAOH,SAAS,aAAKiB;YAAMb,UAAU;aAASa;IAC/D;IACA,OAAO;QAAEjB;QAAQD,OAAOiB;QAAUN,eAAe;IAAM;AACzD"}
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/definition-canvas-tree.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CANVAS_ROOT_ELEMENT_ID } from '../foundation/constants/canvas'\n\ntype Nodes = Record<string, any>\n\n/**\n * Adapt a reusable-component definition for the canvas, and back (AGL-680).\n *\n * The canvas requires its root to be the canonical `_@_` node — `setNodes`\n * stores whatever it is handed and `rootNode` looks up that exact id, so a\n * tree without it has no root and renders nothing.\n *\n * A definition's root is NOT `_@_`: `promoteToComponent` walks a selected\n * subtree, so the root is whichever node was selected. Loading one straight\n * into the canvas therefore produced an empty editor with \"Error rendering\n * undefined\" in the console — found by the editor smoke test, not by\n * typechecking, because both shapes are `Record<string, node>`.\n *\n * So the definition is wrapped under a synthetic canvas root on the way in\n * and unwrapped on the way out. The wrapper is never published: the tenant\n * runtime grafts from `rootId`, and an extra always-empty container in\n * every instance would be both wrong and wasteful.\n *\n * A map with no findable root always yields the empty canvas root rather\n * than being passed through (AGL-753). Passing it through renders `'Invalid\n * node'` with Add Element disabled — an editor with no way out, so the\n * document can never be repaired from the UI. Components created before\n * AGL-693 are stored as exactly that (`nodes: {}`), which is why this is a\n * load-time repair and not a migration.\n */\nexport function definitionToCanvasTree(definition: {\n rootId?: string\n nodes?: Nodes\n}): Nodes {\n const nodes = definition?.nodes ?? {}\n // Already canvas-shaped (a tree saved by this editor) — leave it alone.\n if (nodes[CANVAS_ROOT_ELEMENT_ID]) return nodes\n\n const rootId =\n definition?.rootId && nodes[definition.rootId]\n ? definition.rootId\n : (Object.keys(nodes).find((id) => !nodes[id]?.parentId) ??\n // A definition promoted out of a screen can keep the `parentId` it\n // had there, so \"no parent\" is not always \"no parentId\" — a parent\n // that is not itself in the map is just as much a root.\n Object.keys(nodes).find((id) => !nodes[nodes[id]?.parentId]))\n if (!rootId) {\n return {\n [CANVAS_ROOT_ELEMENT_ID]: {\n $id: CANVAS_ROOT_ELEMENT_ID,\n componentId: 'div',\n parentId: null,\n nodes: [],\n },\n }\n }\n\n return {\n ...nodes,\n [rootId]: { ...nodes[rootId], parentId: CANVAS_ROOT_ELEMENT_ID },\n [CANVAS_ROOT_ELEMENT_ID]: {\n $id: CANVAS_ROOT_ELEMENT_ID,\n componentId: 'box',\n parentId: null,\n nodes: [rootId],\n },\n }\n}\n\n/**\n * Recover the definition shape from a canvas tree.\n *\n * Returns the single child of the synthetic root as `rootId` and drops the\n * wrapper. A canvas root with several children means the author added\n * siblings at the top level — there is no single definition root to name,\n * so the wrapper is kept and reported, letting the caller refuse rather\n * than silently publishing only the first child.\n */\nexport function canvasTreeToDefinition(nodes: Nodes | undefined): {\n rootId?: string\n nodes: Nodes\n ambiguousRoot: boolean\n} {\n const map = nodes ?? {}\n const canvasRoot = map[CANVAS_ROOT_ELEMENT_ID]\n if (!canvasRoot) {\n const rootId = Object.keys(map).find((id) => !map[id]?.parentId)\n return { rootId, nodes: map, ambiguousRoot: false }\n }\n\n const children: string[] = Array.isArray(canvasRoot.nodes)\n ? canvasRoot.nodes.filter((id: string) => map[id])\n : []\n if (children.length !== 1) {\n return {\n rootId: CANVAS_ROOT_ELEMENT_ID,\n nodes: map,\n ambiguousRoot: true,\n }\n }\n\n const rootId = children[0]\n const stripped: Nodes = {}\n for (const [id, node] of Object.entries(map)) {\n if (id === CANVAS_ROOT_ELEMENT_ID) continue\n stripped[id] = id === rootId ? { ...node, parentId: null } : node\n }\n return { rootId, nodes: stripped, ambiguousRoot: false }\n}\n\n/** One element of a nested tree, as an element preset's `data` holds it. */\ninterface NestedNode {\n $id?: string | null\n nodes?: NestedNode[]\n [key: string]: unknown\n}\n\n/**\n * A nested tree — an element preset's `data` — as a definition: a node map\n * keyed by fresh ids, with `rootId` naming the outermost element (AGL-3287).\n *\n * What a new component is seeded with when it starts from a preset, such as\n * a Header email block starting from the email plugin's Header. The canvas\n * mints ids the same way when the preset is dropped into a document, so a\n * component started from a preset and one promoted from a dropped copy of it\n * hold the same tree.\n *\n * Values are copied, never shared: a registered preset is a live object the\n * drawer keeps reading, and a definition that aliased its props would carry\n * every later edit back into the preset.\n */\nexport function nestedToDefinition(\n nested: NestedNode,\n createId: () => string,\n): { rootId: string; nodes: Nodes } {\n const nodes: Nodes = {}\n const visit = (node: NestedNode, parentId: string | null): string => {\n const { $id: _presetId, nodes: children, ...fields } = node\n const id = createId()\n const entry: Nodes[string] = {\n ...(JSON.parse(JSON.stringify(fields)) as Record<string, unknown>),\n $id: id,\n parentId,\n }\n nodes[id] = entry\n if (Array.isArray(children) && children.length) {\n entry.nodes = children.map((child) => visit(child, id))\n }\n return id\n }\n const rootId = visit(nested ?? {}, null)\n return { rootId, nodes }\n}\n"],"names":["CANVAS_ROOT_ELEMENT_ID","definitionToCanvasTree","definition","Object","nodes","rootId","keys","find","id","parentId","$id","componentId","canvasTreeToDefinition","map","canvasRoot","ambiguousRoot","children","Array","isArray","filter","length","stripped","node","entries","nestedToDefinition","nested","createId","visit","_presetId","fields","entry","JSON","parse","stringify","child"],"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,OAAO;QACPC,SAAS;IACX;IACA6C,mBAAmB;QACjB/C,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA8C,WAAW;QACThD,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACA+C,iBAAiB;QACfjD,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAgD,WAAW;QACTlD,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;IACAiD,UAAU;QACRnD,MAAM;QACNC,OAAO;QACPC,SAAS;IACX;AACF,EAAoD;AAIpD,OAAO,MAAMkD,sBAAsB;IACjCrD,cAAc;QAAC;QAA4B;QAAuB;QAAS;QAAgB;QAAa;QAAe;QAAS;QAAoB;QAAyB;QAAyB;QAAsB;QAA+B;QAAe;KAAW;IACrRI,gBAAgB;QAAC;QAAqB;QAAY;QAAgB;QAAa;QAAe;QAAkC;QAAoC;QAAU;QAAc;QAAoB;QAAc;QAAgB;QAA8B;QAAmC;QAAsB;KAAW;IAChVC,aAAa;QAAC;QAAmB;QAAa;QAAyC;QAA0B;QAA0B;QAA8B;QAA4C;QAAmB;QAAa;KAAW;IAChQC,eAAe;QAAC;QAAU;QAAY;QAAqB;QAAiB;QAAmB;KAAW;IAC1GC,OAAO;QAAC;QAAuB;QAAkD;QAAmB;QAAkB;QAAiB;QAAmB;KAAW;IACrKC,eAAe;QAAC;QAA0B;QAAmB;QAAoB;QAA2B;QAAkB;QAAgB;QAAc;QAAc;QAAiB;QAAmB;KAAW;IACzNC,cAAc;QAAC;QAAgB;QAAe;QAAgB;QAA0B;QAAW;KAAW;IAC9GC,YAAY;QAAC;QAA0B;QAAiC;QAA6B;QAA4B;QAAqC;QAA6B;QAAoC;QAAmB;KAAW;IACrQC,OAAO;QAAC;QAA0B;QAA6B;QAAyB;QAAoB;QAAwB;QAAgC;KAAW;IAC/KC,UAAU;QAAC;QAAmD;QAAqB;QAAwB;QAAoB;QAAmB;KAAW;IAC7JC,eAAe;QAAC;QAAuC;QAAU;QAAyD;QAAyB;QAAmB;QAAc;QAAwC;QAA+B;QAAmC;QAAgC;KAAW;IACzUC,SAAS;QAAC;QAAwB;QAAe;QAAmC;QAAiB;QAA2B;QAAkB;QAAoB;QAAwC;QAAmB;QAAwC;QAAuB;QAAoB;QAAqB;QAAiB;QAAU;QAAgB;QAAkB;QAAqB;QAAsB;QAAe;QAAa;QAAgB;QAAe;QAAkB;QAAoB;QAAoB;QAAY;QAAc;QAAkB;KAAW;IAC7lBC,UAAU;QAAC;QAAmB;QAA0B;QAAsB;QAAgB;QAA8B;QAAiB;QAAoB;QAAsB;QAAoC;QAA6B;QAAuB;QAAc;KAAW;IACxSC,UAAU;QAAC;QAAoB;QAAoB;QAAc;QAAsB;QAAgB;QAAW;QAAyB;QAA4B;KAAW;IAClLC,gBAAgB;QAAC;QAA8B;QAAuB;QAAgB;QAAY;QAAoB;QAAyB;QAAS;KAAW;IACnKC,SAAS;QAAC;QAAkC;QAAoC;QAAO;QAAwB;QAAgB;QAAU;QAAyB;KAAW;IAC7KC,UAAU;QAAC;QAAiB;QAAc;QAAmB;QAAoB;QAAe;QAAwB;QAAW;QAAkB;QAAmB;QAAsB;QAAmB;QAAoB;QAAgC;QAAyB;QAAyB;QAAiB;KAAW;IACnVC,kBAAkB;QAAC;QAAuB;QAAsB;QAA4B;QAAoC;QAAmB;QAA8B;QAAyB;QAAqC;QAAkC;QAAuC;KAAW;IACnUC,WAAW;QAAC;QAAuB;QAAqB;QAAoB;QAA0B;QAAsB;QAAoB;QAAe;QAAuB;QAA0B;QAAU;KAAW;IACrOC,aAAa;QAAC;QAAgB;QAAoB;QAAuB;QAAwB;QAAmB;QAAoB;QAA4B;QAAwB;QAAsB;QAAS;QAAsC;QAA2B;QAA0B;QAAuB;QAAiC;QAAkB;KAAyB;IACzZC,mBAAmB;QAAC;QAA0B;QAAmB;QAAwB;QAA2B;QAAkB;QAAqB;QAAoB;QAAmB;QAAiC;QAA6B;KAAW;IAC3QC,eAAe;QAAC;QAAmB;QAAsB;QAAsB;QAAsB;QAAuB;QAAiB;QAA0B;QAA4B;KAAW;IAC9MC,eAAe;QAAC;QAA6B;QAAoB;QAAyB;QAAmC;QAAwB;QAAsB;QAAU;QAAiB;QAAqB;QAA8B;QAAsB;QAAU;KAAW;IACpSC,UAAU;QAAC;QAA0B;QAAsB;QAA4B;QAAsB;QAAoB;QAAa;QAAgC;QAAoB;QAA8B;QAAyB;KAAW;IACpQC,mBAAmB;QAAC;QAA2B;QAAiB;QAAW;QAAuB;QAAyB;QAAuB;QAAsB;KAAW;IACnLC,UAAU;QAAC;QAAsB;QAAsB;QAA0B;QAAmB;QAAgC;QAA0B;QAAe;QAAoB;QAAoB;QAAiB;QAAgB;QAAsB;QAAwB;QAAuB;QAAoB;KAAW;IAC1VC,YAAY;QAAC;QAAsB;QAAa;QAA0B;QAAyB;QAAgB;QAAa;QAA4B;QAAiB;QAA0B;QAAyB;QAAU;QAAsB;QAAoB;QAAgC;KAAW;IAC/TC,aAAa;QAAC;QAAc;QAA6C;QAAkB;QAAqB;QAAgB;QAAoB;QAAkB;QAA2B;QAAY;KAAW;IACxNC,UAAU;QAAC;QAAmB;QAAsB;QAAoB;QAA2C;QAAoB;QAAoB;QAAqC;QAA6B;QAAuB;QAAuC;QAAc;QAA0B;QAAkB;QAAqB;QAAmB;QAAuB;QAAoB;KAAW;IACnbC,UAAU;QAAC;QAAsB;QAAqB;QAAY;QAAiC;QAA+B;QAAqB;QAAuB;QAAkB;KAAW;IAC3MC,UAAU;QAAC;QAAkB;QAAoB;QAAc;QAAgB;QAA0B;QAAiC;QAAmB;KAAW;IACxKC,OAAO;QAAC;QAAc;QAAW;QAA4B;QAAoB;QAAoB;QAAe;QAA8B;QAA4C;QAAiB;QAAU;KAAW;IACpOC,gBAAgB;QAAC;QAAsB;QAAyB;QAAyB;QAAiB;QAAY;QAA2B;KAA8B;IAC/KC,gBAAgB;QAAC;QAAoB;QAAyC;QAA6B;QAA2B;QAA+B;QAA6B;QAAoB;QAA+B;QAA6B;QAA6B;QAAoC;QAAkB;QAAY;QAAgB;QAAsB;QAAqB;QAAgC;QAAoB;QAAuC;QAAoB;QAAuB;QAAgB;QAAiB;QAAiB;QAAkB;QAAkB;QAAuB;QAA4B;QAAgB;QAA6B;QAAkB;QAAwB;QAA2B;QAA6B;QAA4B;QAAgB;QAAe;QAAqB;QAAW;QAAoB;QAAuB;QAAkB;QAAmB;QAAkB;QAAiB;QAAsB;QAA0B;KAAW;IACnlCC,QAAQ;QAAC;QAAkB;QAA4B;QAAmB;KAAW;IACrFC,OAAO;QAAC;QAAkC;QAAiB;QAA+B;QAA8B;QAAiC;QAAgB;QAA4B;QAA2B;QAAiB;QAAkD;QAAyB;QAAc;QAA6B;QAAwB;QAA6B;QAA8B;QAAuB;QAAqB;QAAkC;KAAW;IACnhBC,wBAAwB;QAAC;QAAqB;QAAgB;QAAkB;QAAmB;QAAqB;QAAmB;QAAe;QAAe;QAAoB;KAAW;IACxMC,iBAAiB;QAAC;QAAmB;QAAoB;QAAkB;QAAwB;QAAuD;QAAyB;QAAiD;QAAsC;QAAuC;QAAiB;QAAe;QAAa;QAAiB;QAAS;KAAW;IACnYC,iBAAiB;QAAC;QAAiD;QAAwC;QAAuD;QAA+B;QAA4B;QAA0B;KAA8C;IACrSC,mBAAmB;QAAC;QAAqB;QAAuB;QAAc;QAAa;QAAiD;QAAsB;QAAqB;QAAsB;KAAW;IACxNC,aAAa;QAAC;QAAyB;QAAuC;QAAwB;QAAkB;QAAwB;QAAmC;QAAS;KAAW;IACvMC,gBAAgB;QAAC;QAA8B;QAAe;QAAa;QAAU;QAAyB;QAAuC;QAAgC;KAAW;IAChMC,SAAS;QAAC;QAAqB;QAAgB;QAAmB;QAAsC;QAAoB;QAAuC;QAA0C;QAAc;QAAmB;QAAqB;KAAW;IAC9QC,KAAK;QAAC;QAAc;QAAiB;QAAkC;QAA2B;QAA+B;QAAiB;KAAW;IAC7JC,gBAAgB;QAAC;QAAyB;QAAoB;QAAkB;QAA2B;QAAS;KAAW;IAC/HC,mBAAmB;QAAC;QAA8B;QAA4B;QAA0B;QAAmC;QAAuC;QAAyB;QAAuB;QAA0C;QAA8C;QAA0B;QAA4C;QAAiC;QAAsB;QAAoB;QAA2B;QAA0C;QAAwB;QAA2B;QAAqC;QAAiB;KAA6B;IACtpBC,WAAW;QAAC;QAAqB;QAAqC;QAAY;QAAmB;KAAW;IAChHC,iBAAiB;QAAC;QAAsB;QAAgC;QAAqC;QAA+B;QAAmC;KAAW;IAC1LC,WAAW;QAAC;QAAmB;QAA+B;QAAmB;QAAsB;QAAY;QAAc;QAAmB;QAAe;QAAoB;QAAiB;QAAwB;QAAsB;QAA2B;QAAc;QAAqB;QAAoB;QAAW;QAAkB;QAAgB;QAAY;QAA0B;QAAgB;KAAW;IACtbC,UAAU;QAAC;QAAsB;QAAqB;QAAS;KAAW;AAC5E,EAA4E"}