@aglyn/plugins-ai 1.0.0-beta.217 → 1.0.0-beta.219

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 (187) hide show
  1. package/README.md +2 -2
  2. package/package.json +17 -17
  3. package/src/index.d.ts +1 -0
  4. package/src/index.js.map +1 -1
  5. package/src/lib/activity/ai-activity-actions.js +10 -2
  6. package/src/lib/activity/ai-activity-actions.js.map +1 -1
  7. package/src/lib/components/ai-assist-provider.component.js +2 -2
  8. package/src/lib/components/ai-assist-provider.component.js.map +1 -1
  9. package/src/lib/components/ai-brief-dialog.component.js +3 -3
  10. package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
  11. package/src/lib/components/ai-experiment-cards.component.js +1 -1
  12. package/src/lib/components/ai-experiment-cards.component.js.map +1 -1
  13. package/src/lib/components/ai-job-plan.component.d.ts +8 -0
  14. package/src/lib/components/ai-job-plan.component.js +21 -4
  15. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  16. package/src/lib/components/ai-permissions-on-host.component.d.ts +12 -2
  17. package/src/lib/components/ai-permissions-on-host.component.js +12 -3
  18. package/src/lib/components/ai-permissions-on-host.component.js.map +1 -1
  19. package/src/lib/components/ai-product-copy-card.component.d.ts +1 -1
  20. package/src/lib/components/ai-product-copy-card.component.js.map +1 -1
  21. package/src/lib/components/ai-product-import-option.component.d.ts +1 -1
  22. package/src/lib/components/ai-product-import-option.component.js.map +1 -1
  23. package/src/lib/components/ai-product-zones.d.ts +124 -0
  24. package/src/lib/components/ai-product-zones.js +32 -0
  25. package/src/lib/components/ai-product-zones.js.map +1 -0
  26. package/src/lib/components/ai-products-hub-card.component.d.ts +1 -1
  27. package/src/lib/components/ai-products-hub-card.component.js.map +1 -1
  28. package/src/lib/components/ai-products-proposals.component.d.ts +1 -1
  29. package/src/lib/components/ai-products-proposals.component.js.map +1 -1
  30. package/src/lib/components/ai-seo-audit-card.component.js +35 -92
  31. package/src/lib/components/ai-seo-audit-card.component.js.map +1 -1
  32. package/src/lib/components/ai-seo-fields-card.component.js.map +1 -1
  33. package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
  34. package/src/lib/components/assist-jobs-drawer.component.js +21 -15
  35. package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
  36. package/src/lib/components/assist-panel.component.js +2 -2
  37. package/src/lib/components/assist-panel.component.js.map +1 -1
  38. package/src/lib/components/assist-signals-page.component.js +2 -2
  39. package/src/lib/components/assist-signals-page.component.js.map +1 -1
  40. package/src/lib/jobs/ai-email-bindings.d.ts +28 -21
  41. package/src/lib/jobs/ai-email-bindings.js +75 -42
  42. package/src/lib/jobs/ai-email-bindings.js.map +1 -1
  43. package/src/lib/jobs/ai-job-campaign-step.d.ts +0 -2
  44. package/src/lib/jobs/ai-job-campaign-step.js +5 -10
  45. package/src/lib/jobs/ai-job-campaign-step.js.map +1 -1
  46. package/src/lib/jobs/ai-job-component-step.js +9 -3
  47. package/src/lib/jobs/ai-job-component-step.js.map +1 -1
  48. package/src/lib/jobs/ai-job-drafts.d.ts +9 -1
  49. package/src/lib/jobs/ai-job-drafts.js +12 -5
  50. package/src/lib/jobs/ai-job-drafts.js.map +1 -1
  51. package/src/lib/jobs/ai-job-email-step.d.ts +0 -2
  52. package/src/lib/jobs/ai-job-email-step.js +1 -3
  53. package/src/lib/jobs/ai-job-email-step.js.map +1 -1
  54. package/src/lib/jobs/ai-job-experiment-step.d.ts +1 -1
  55. package/src/lib/jobs/ai-job-experiment-step.js +1 -1
  56. package/src/lib/jobs/ai-job-experiment-step.js.map +1 -1
  57. package/src/lib/jobs/ai-job-generation.d.ts +1 -1
  58. package/src/lib/jobs/ai-job-generation.js +10 -2
  59. package/src/lib/jobs/ai-job-generation.js.map +1 -1
  60. package/src/lib/jobs/ai-job-layout-step.js +1 -1
  61. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  62. package/src/lib/jobs/ai-job-page-sections.d.ts +4 -2
  63. package/src/lib/jobs/ai-job-page-sections.js +7 -4
  64. package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
  65. package/src/lib/jobs/ai-job-page-step.js +7 -2
  66. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  67. package/src/lib/jobs/ai-job-plan-conformance.js +33 -0
  68. package/src/lib/jobs/ai-job-plan-conformance.js.map +1 -1
  69. package/src/lib/jobs/ai-job-plan-step.d.ts +3 -0
  70. package/src/lib/jobs/ai-job-plan-step.js +29 -17
  71. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  72. package/src/lib/jobs/ai-job-plugin-drafts.d.ts +14 -6
  73. package/src/lib/jobs/ai-job-plugin-drafts.js +12 -4
  74. package/src/lib/jobs/ai-job-plugin-drafts.js.map +1 -1
  75. package/src/lib/jobs/ai-job-products-step.d.ts +7 -0
  76. package/src/lib/jobs/ai-job-products-step.js +54 -32
  77. package/src/lib/jobs/ai-job-products-step.js.map +1 -1
  78. package/src/lib/jobs/ai-job-seo-step.d.ts +3 -24
  79. package/src/lib/jobs/ai-job-seo-step.js +35 -108
  80. package/src/lib/jobs/ai-job-seo-step.js.map +1 -1
  81. package/src/lib/jobs/ai-job-template-step.js +1 -0
  82. package/src/lib/jobs/ai-job-template-step.js.map +1 -1
  83. package/src/lib/jobs/ai-job-theme-step.d.ts +1 -1
  84. package/src/lib/jobs/ai-job-theme-step.js +1 -1
  85. package/src/lib/jobs/ai-job-theme-step.js.map +1 -1
  86. package/src/lib/jobs/ai-job-workflow-step.js +17 -9
  87. package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
  88. package/src/lib/jobs/ai-workflow-records.d.ts +19 -11
  89. package/src/lib/jobs/ai-workflow-records.js +60 -27
  90. package/src/lib/jobs/ai-workflow-records.js.map +1 -1
  91. package/src/lib/model/ai-allotments.d.ts +11 -3
  92. package/src/lib/model/ai-allotments.js +21 -4
  93. package/src/lib/model/ai-allotments.js.map +1 -1
  94. package/src/lib/model/ai-automation-outline.d.ts +21 -2
  95. package/src/lib/model/ai-automation-outline.js.map +1 -1
  96. package/src/lib/model/ai-build-plan.d.ts +57 -0
  97. package/src/lib/model/ai-build-plan.js +116 -7
  98. package/src/lib/model/ai-build-plan.js.map +1 -1
  99. package/src/lib/model/ai-seo.d.ts +11 -51
  100. package/src/lib/model/ai-seo.js +0 -17
  101. package/src/lib/model/ai-seo.js.map +1 -1
  102. package/src/lib/model/ai-site-inventory.js +1 -1
  103. package/src/lib/model/ai-site-inventory.js.map +1 -1
  104. package/src/lib/model/ai-template-subjects.js +2 -2
  105. package/src/lib/model/ai-template-subjects.js.map +1 -1
  106. package/src/lib/model/ai-theme-proposal.d.ts +1 -1
  107. package/src/lib/model/ai-theme-proposal.js +1 -1
  108. package/src/lib/model/ai-theme-proposal.js.map +1 -1
  109. package/src/lib/model/ai-workflow-job.d.ts +1 -3
  110. package/src/lib/model/ai-workflow-job.js +1 -2
  111. package/src/lib/model/ai-workflow-job.js.map +1 -1
  112. package/src/lib/plan-entitlements.d.ts +149 -0
  113. package/src/lib/plan-entitlements.js +168 -0
  114. package/src/lib/plan-entitlements.js.map +1 -0
  115. package/src/lib/plugin.js +1 -1
  116. package/src/lib/plugin.js.map +1 -1
  117. package/src/lib/runtime/ai-doctrine-validators.d.ts +25 -4
  118. package/src/lib/runtime/ai-doctrine-validators.js +179 -23
  119. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  120. package/src/lib/runtime/ai-doctrine.js +5 -5
  121. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  122. package/src/lib/runtime/ai-eval-live.js +29 -14
  123. package/src/lib/runtime/ai-eval-live.js.map +1 -1
  124. package/src/lib/runtime/ai-eval.d.ts +8 -1
  125. package/src/lib/runtime/ai-eval.js +29 -2
  126. package/src/lib/runtime/ai-eval.js.map +1 -1
  127. package/src/lib/runtime/ai-node-tree.js +3 -3
  128. package/src/lib/runtime/ai-node-tree.js.map +1 -1
  129. package/src/lib/runtime/ai-palette.d.ts +6 -0
  130. package/src/lib/runtime/ai-palette.generated.js +21 -255
  131. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  132. package/src/lib/runtime/ai-palette.js +7 -1
  133. package/src/lib/runtime/ai-palette.js.map +1 -1
  134. package/src/lib/runtime/seo-audit.d.ts +13 -63
  135. package/src/lib/runtime/seo-audit.js +22 -207
  136. package/src/lib/runtime/seo-audit.js.map +1 -1
  137. package/src/lib/runtime/site-inventory.js +1 -1
  138. package/src/lib/runtime/site-inventory.js.map +1 -1
  139. package/src/lib/server/ai-admin-org.js +4 -25
  140. package/src/lib/server/ai-admin-org.js.map +1 -1
  141. package/src/lib/server/ai-admin-user.js +2 -19
  142. package/src/lib/server/ai-admin-user.js.map +1 -1
  143. package/src/lib/server/ai-assist-prompts.js +3 -3
  144. package/src/lib/server/ai-assist-prompts.js.map +1 -1
  145. package/src/lib/server/ai-insight-digest-route.js +8 -1
  146. package/src/lib/server/ai-insight-digest-route.js.map +1 -1
  147. package/src/lib/server/assist-chat.js +11 -10
  148. package/src/lib/server/assist-chat.js.map +1 -1
  149. package/src/lib/server/assist-view-context.js +23 -23
  150. package/src/lib/server/assist-view-context.js.map +1 -1
  151. package/src/lib/server.d.ts +1 -0
  152. package/src/lib/server.js.map +1 -1
  153. package/src/lib/testing/stand-in-automation-index.d.ts +20 -0
  154. package/src/lib/testing/stand-in-automation-index.js +102 -0
  155. package/src/lib/testing/stand-in-automation-index.js.map +1 -0
  156. package/src/lib/testing/stand-in-list-send-time.d.ts +4 -0
  157. package/src/lib/testing/stand-in-list-send-time.js +77 -0
  158. package/src/lib/testing/stand-in-list-send-time.js.map +1 -0
  159. package/src/lib/testing/stand-in-pipeline-index.d.ts +32 -0
  160. package/src/lib/testing/stand-in-pipeline-index.js +59 -0
  161. package/src/lib/testing/stand-in-pipeline-index.js.map +1 -0
  162. package/src/lib/testing/stand-in-product-index.d.ts +20 -0
  163. package/src/lib/testing/stand-in-product-index.js +105 -0
  164. package/src/lib/testing/stand-in-product-index.js.map +1 -0
  165. package/src/lib/tools/ai-component-tool.js +2 -2
  166. package/src/lib/tools/ai-component-tool.js.map +1 -1
  167. package/src/lib/tools/ai-duplicate-resource-tool.js +1 -1
  168. package/src/lib/tools/ai-duplicate-resource-tool.js.map +1 -1
  169. package/src/lib/tools/ai-seo-tool.d.ts +6 -18
  170. package/src/lib/tools/ai-seo-tool.js +7 -38
  171. package/src/lib/tools/ai-seo-tool.js.map +1 -1
  172. package/src/lib/tools/ai-workflow-tool.d.ts +1 -1
  173. package/src/lib/tools/ai-workflow-tool.js +1 -1
  174. package/src/lib/tools/ai-workflow-tool.js.map +1 -1
  175. package/src/lib/usage/ai-allotment-alerts.d.ts +3 -0
  176. package/src/lib/usage/ai-allotment-alerts.js +20 -8
  177. package/src/lib/usage/ai-allotment-alerts.js.map +1 -1
  178. package/src/lib/usage/assist-free-taste.js +2 -1
  179. package/src/lib/usage/assist-free-taste.js.map +1 -1
  180. package/src/lib/usage/assist-signal-mining.js +1 -1
  181. package/src/lib/usage/assist-signal-mining.js.map +1 -1
  182. package/src/lib/usage-axes.d.ts +39 -0
  183. package/src/lib/usage-axes.js +70 -0
  184. package/src/lib/usage-axes.js.map +1 -0
  185. package/src/lib/runtime/seo-page-facts.d.ts +0 -61
  186. package/src/lib/runtime/seo-page-facts.js +0 -150
  187. package/src/lib/runtime/seo-page-facts.js.map +0 -1
@@ -1,150 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2026 Aglyn LLC
4
- *
5
- * Licensed under the Apache License, Version 2.0 (the "License");
6
- * you may not use this file except in compliance with the License.
7
- * You may obtain a copy of the License at
8
- *
9
- * http://www.apache.org/licenses/LICENSE-2.0
10
- *
11
- * Unless required by applicable law or agreed to in writing, software
12
- * distributed under the License is distributed on an "AS IS" BASIS,
13
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- * See the License for the specific language governing permissions and
15
- * limitations under the License.
16
- */ import { buildPageMarkdown, headingLevelOf, isPageChromeNode, pageContentRootId } from "@aglyn/aglyn/app-utils/page-markdown";
17
- /**
18
- * What a page SAYS, read off its node tree (AGL-2910): the text a search
19
- * listing is written from, the headings, and the images — each heading and
20
- * image by node id, because a content fix addresses the element it changes.
21
- *
22
- * The text is the page's Markdown representation, the one an agent is
23
- * served, so what the listing summarizes is what a reader of the page gets.
24
- * The walk starts where that serializer starts — the content region — and
25
- * skips what it skips, through the same chrome predicate: a heading in the
26
- * site's navigation is not the page's heading.
27
- */ /** How much of a page's text a prompt carries. A listing summarizes; it does not need the page. */ export const AI_SEO_PAGE_TEXT_MAX_CHARS = 2000;
28
- /** How much text around an image describes it. */ const IMAGE_CONTEXT_MAX_CHARS = 160;
29
- /** The deepest a walk goes, and it stops at a cycle — a node map is data. */ const MAX_DEPTH = 64;
30
- /** The text components whose heading level `headingLevelOf` reads. */ const TEXT_COMPONENTS = new Set([
31
- 'muiTypography',
32
- 'muiInlineText',
33
- 'muiListItemText'
34
- ]);
35
- const collapse = (value)=>value.replace(/\s+/g, ' ').trim();
36
- /** Author HTML to its text, for a heading stored as rich text. */ function htmlText(html) {
37
- return collapse(html.replace(/<[^>]*>/g, ' ').replace(/&nbsp;/g, ' ').replace(/&amp;/g, '&'));
38
- }
39
- /** A text component's own text, and whether it is plain (a fix can rewrite it). */ function textOf(props) {
40
- const html = props['html'];
41
- if (typeof html === 'string' && html.trim()) return {
42
- text: htmlText(html),
43
- plain: false
44
- };
45
- const children = props['children'];
46
- return typeof children === 'string' ? {
47
- text: collapse(children),
48
- plain: true
49
- } : {
50
- text: '',
51
- plain: true
52
- };
53
- }
54
- const childIdsOf = (node)=>Array.isArray(node == null ? void 0 : node.nodes) ? (node == null ? void 0 : node.nodes).filter((id)=>typeof id === 'string') : [];
55
- /** Read the facts a listing and an audit need from one page's node map. */ export function aiSeoPageFacts(nodes, options = {}) {
56
- var _options_maxChars;
57
- const empty = {
58
- text: '',
59
- wordCount: 0,
60
- headings: [],
61
- h1s: [],
62
- images: [],
63
- imagesMissingAlt: [],
64
- contentRootId: null
65
- };
66
- if (!nodes) return empty;
67
- const contentRootId = pageContentRootId(nodes, options.rootId);
68
- if (!contentRootId) return empty;
69
- const markdown = buildPageMarkdown({
70
- nodes,
71
- rootId: options.rootId
72
- });
73
- const text = markdown.trim().slice(0, (_options_maxChars = options.maxChars) != null ? _options_maxChars : AI_SEO_PAGE_TEXT_MAX_CHARS);
74
- const headings = [];
75
- const images = [];
76
- let lastText = '';
77
- const seen = new Set();
78
- const visit = (id, depth)=>{
79
- var _node_props, _node_componentId, _props_html;
80
- if (depth > MAX_DEPTH || seen.has(id)) return;
81
- const node = nodes[id];
82
- if (!node) return;
83
- seen.add(id);
84
- if (isPageChromeNode(node)) return;
85
- const props = (_node_props = node.props) != null ? _node_props : {};
86
- const componentId = String((_node_componentId = node.componentId) != null ? _node_componentId : '');
87
- if (TEXT_COMPONENTS.has(componentId)) {
88
- const { text: own, plain } = textOf(props);
89
- const level = headingLevelOf(props);
90
- if (own) {
91
- if (level > 0) headings.push({
92
- nodeId: id,
93
- level,
94
- text: own,
95
- editable: plain
96
- });
97
- lastText = own;
98
- }
99
- } else if (componentId === 'markdown' && typeof props['content'] === 'string') {
100
- for (const line of String(props['content']).split('\n')){
101
- const match = /^(#{1,6})\s+(.+)$/.exec(line.trim());
102
- if (match) {
103
- headings.push({
104
- nodeId: null,
105
- level: match[1].length,
106
- text: collapse(match[2]),
107
- editable: false
108
- });
109
- }
110
- }
111
- lastText = collapse(String(props['content'])).slice(0, IMAGE_CONTEXT_MAX_CHARS);
112
- } else if (componentId === 'custom-html' && typeof ((_props_html = props['html']) != null ? _props_html : props['children']) === 'string') {
113
- var _props_html1;
114
- const html = String((_props_html1 = props['html']) != null ? _props_html1 : props['children']);
115
- for (const match of html.matchAll(/<h([1-6])[^>]*>([\s\S]*?)<\/h\1>/gi)){
116
- headings.push({
117
- nodeId: null,
118
- level: Number(match[1]),
119
- text: htmlText(match[2]),
120
- editable: false
121
- });
122
- }
123
- } else if (componentId === 'image') {
124
- var _props_src;
125
- const src = String((_props_src = props['src']) != null ? _props_src : '').trim();
126
- if (src) {
127
- var _props_alt;
128
- images.push({
129
- nodeId: id,
130
- src,
131
- alt: collapse(String((_props_alt = props['alt']) != null ? _props_alt : '')),
132
- context: lastText.slice(0, IMAGE_CONTEXT_MAX_CHARS)
133
- });
134
- }
135
- }
136
- for (const childId of childIdsOf(node))visit(childId, depth + 1);
137
- };
138
- visit(contentRootId, 0);
139
- return {
140
- text,
141
- wordCount: text ? text.split(/\s+/).filter(Boolean).length : 0,
142
- headings,
143
- h1s: headings.filter((heading)=>heading.level === 1),
144
- images,
145
- imagesMissingAlt: images.filter((image)=>!image.alt),
146
- contentRootId
147
- };
148
- }
149
-
150
- //# sourceMappingURL=seo-page-facts.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/runtime/seo-page-facts.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n buildPageMarkdown,\n headingLevelOf,\n isPageChromeNode,\n pageContentRootId,\n type PageMarkdownNode,\n type PageMarkdownNodes,\n} from '@aglyn/aglyn/app-utils/page-markdown'\n\n/**\n * What a page SAYS, read off its node tree (AGL-2910): the text a search\n * listing is written from, the headings, and the images — each heading and\n * image by node id, because a content fix addresses the element it changes.\n *\n * The text is the page's Markdown representation, the one an agent is\n * served, so what the listing summarizes is what a reader of the page gets.\n * The walk starts where that serializer starts — the content region — and\n * skips what it skips, through the same chrome predicate: a heading in the\n * site's navigation is not the page's heading.\n */\n\n/** How much of a page's text a prompt carries. A listing summarizes; it does not need the page. */\nexport const AI_SEO_PAGE_TEXT_MAX_CHARS = 2_000\n\n/** How much text around an image describes it. */\nconst IMAGE_CONTEXT_MAX_CHARS = 160\n\n/** The deepest a walk goes, and it stops at a cycle — a node map is data. */\nconst MAX_DEPTH = 64\n\n/** The text components whose heading level `headingLevelOf` reads. */\nconst TEXT_COMPONENTS: ReadonlySet<string> = new Set(['muiTypography', 'muiInlineText', 'muiListItemText'])\n\nexport interface AiSeoHeading {\n /** The element, or `null` for a heading inside rich text a fix cannot address. */\n nodeId: string | null\n level: number\n text: string\n /** Whether a content fix can change it: a text component with plain text. */\n editable: boolean\n}\n\nexport interface AiSeoImage {\n nodeId: string\n src: string\n alt: string\n /** The nearest heading or text before it, which is what the image illustrates. */\n context: string\n}\n\nexport interface AiSeoPageFacts {\n /** The page's Markdown body, capped. */\n text: string\n wordCount: number\n headings: AiSeoHeading[]\n h1s: AiSeoHeading[]\n images: AiSeoImage[]\n imagesMissingAlt: AiSeoImage[]\n /** The node the content region starts at; a new heading goes first inside it. */\n contentRootId: string | null\n}\n\nconst collapse = (value: string): string => value.replace(/\\s+/g, ' ').trim()\n\n/** Author HTML to its text, for a heading stored as rich text. */\nfunction htmlText(html: string): string {\n return collapse(html.replace(/<[^>]*>/g, ' ').replace(/&nbsp;/g, ' ').replace(/&amp;/g, '&'))\n}\n\n/** A text component's own text, and whether it is plain (a fix can rewrite it). */\nfunction textOf(props: Record<string, unknown>): { text: string; plain: boolean } {\n const html = props['html']\n if (typeof html === 'string' && html.trim()) return { text: htmlText(html), plain: false }\n const children = props['children']\n return typeof children === 'string' ? { text: collapse(children), plain: true } : { text: '', plain: true }\n}\n\nconst childIdsOf = (node: PageMarkdownNode | undefined): string[] =>\n Array.isArray(node?.nodes) ? (node?.nodes as unknown[]).filter((id): id is string => typeof id === 'string') : []\n\n/** Read the facts a listing and an audit need from one page's node map. */\nexport function aiSeoPageFacts(\n nodes: PageMarkdownNodes | null | undefined,\n options: { rootId?: string; maxChars?: number } = {},\n): AiSeoPageFacts {\n const empty: AiSeoPageFacts = {\n text: '',\n wordCount: 0,\n headings: [],\n h1s: [],\n images: [],\n imagesMissingAlt: [],\n contentRootId: null,\n }\n if (!nodes) return empty\n const contentRootId = pageContentRootId(nodes, options.rootId)\n if (!contentRootId) return empty\n\n const markdown = buildPageMarkdown({ nodes, rootId: options.rootId })\n const text = markdown.trim().slice(0, options.maxChars ?? AI_SEO_PAGE_TEXT_MAX_CHARS)\n const headings: AiSeoHeading[] = []\n const images: AiSeoImage[] = []\n let lastText = ''\n\n const seen = new Set<string>()\n const visit = (id: string, depth: number): void => {\n if (depth > MAX_DEPTH || seen.has(id)) return\n const node = nodes[id]\n if (!node) return\n seen.add(id)\n if (isPageChromeNode(node)) return\n const props = (node.props ?? {}) as Record<string, unknown>\n const componentId = String(node.componentId ?? '')\n if (TEXT_COMPONENTS.has(componentId)) {\n const { text: own, plain } = textOf(props)\n const level = headingLevelOf(props)\n if (own) {\n if (level > 0) headings.push({ nodeId: id, level, text: own, editable: plain })\n lastText = own\n }\n } else if (componentId === 'markdown' && typeof props['content'] === 'string') {\n for (const line of String(props['content']).split('\\n')) {\n const match = /^(#{1,6})\\s+(.+)$/.exec(line.trim())\n if (match) {\n headings.push({ nodeId: null, level: match[1].length, text: collapse(match[2]), editable: false })\n }\n }\n lastText = collapse(String(props['content'])).slice(0, IMAGE_CONTEXT_MAX_CHARS)\n } else if (componentId === 'custom-html' && typeof (props['html'] ?? props['children']) === 'string') {\n const html = String(props['html'] ?? props['children'])\n for (const match of html.matchAll(/<h([1-6])[^>]*>([\\s\\S]*?)<\\/h\\1>/gi)) {\n headings.push({ nodeId: null, level: Number(match[1]), text: htmlText(match[2]), editable: false })\n }\n } else if (componentId === 'image') {\n const src = String(props['src'] ?? '').trim()\n if (src) {\n images.push({\n nodeId: id,\n src,\n alt: collapse(String(props['alt'] ?? '')),\n context: lastText.slice(0, IMAGE_CONTEXT_MAX_CHARS),\n })\n }\n }\n for (const childId of childIdsOf(node)) visit(childId, depth + 1)\n }\n visit(contentRootId, 0)\n\n return {\n text,\n wordCount: text ? text.split(/\\s+/).filter(Boolean).length : 0,\n headings,\n h1s: headings.filter((heading) => heading.level === 1),\n images,\n imagesMissingAlt: images.filter((image) => !image.alt),\n contentRootId,\n }\n}\n"],"names":["buildPageMarkdown","headingLevelOf","isPageChromeNode","pageContentRootId","AI_SEO_PAGE_TEXT_MAX_CHARS","IMAGE_CONTEXT_MAX_CHARS","MAX_DEPTH","TEXT_COMPONENTS","Set","collapse","value","replace","trim","htmlText","html","textOf","props","text","plain","children","childIdsOf","node","Array","isArray","nodes","filter","id","aiSeoPageFacts","options","empty","wordCount","headings","h1s","images","imagesMissingAlt","contentRootId","rootId","markdown","slice","maxChars","lastText","seen","visit","depth","has","add","componentId","String","own","level","push","nodeId","editable","line","split","match","exec","length","matchAll","Number","src","alt","context","childId","Boolean","heading","image"],"mappings":"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"}