aeo.js 0.0.2 → 0.0.3

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 (62) hide show
  1. package/README.md +132 -17
  2. package/dist/angular.d.mts +29 -0
  3. package/dist/angular.d.ts +29 -0
  4. package/dist/angular.js +1314 -0
  5. package/dist/angular.js.map +1 -0
  6. package/dist/angular.mjs +1310 -0
  7. package/dist/angular.mjs.map +1 -0
  8. package/dist/astro.d.mts +8 -2
  9. package/dist/astro.d.ts +8 -2
  10. package/dist/astro.js +342 -100
  11. package/dist/astro.js.map +1 -1
  12. package/dist/astro.mjs +342 -100
  13. package/dist/astro.mjs.map +1 -1
  14. package/dist/cli.d.mts +1 -0
  15. package/dist/cli.d.ts +1 -0
  16. package/dist/cli.js +1880 -0
  17. package/dist/cli.js.map +1 -0
  18. package/dist/cli.mjs +1878 -0
  19. package/dist/cli.mjs.map +1 -0
  20. package/dist/index.d.mts +178 -4
  21. package/dist/index.d.ts +178 -4
  22. package/dist/index.js +972 -19
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.mjs +952 -20
  25. package/dist/index.mjs.map +1 -1
  26. package/dist/next.d.mts +2 -17
  27. package/dist/next.d.ts +2 -17
  28. package/dist/next.js +262 -73
  29. package/dist/next.js.map +1 -1
  30. package/dist/next.mjs +262 -73
  31. package/dist/next.mjs.map +1 -1
  32. package/dist/nuxt.d.mts +13 -0
  33. package/dist/nuxt.d.ts +13 -0
  34. package/dist/nuxt.js +1344 -0
  35. package/dist/nuxt.js.map +1 -0
  36. package/dist/nuxt.mjs +1337 -0
  37. package/dist/nuxt.mjs.map +1 -0
  38. package/dist/react.d.mts +1 -1
  39. package/dist/react.d.ts +1 -1
  40. package/dist/{types-BTY-v-7i.d.mts → types-Cn_Qbkmg.d.mts} +34 -0
  41. package/dist/{types-BTY-v-7i.d.ts → types-Cn_Qbkmg.d.ts} +34 -0
  42. package/dist/vite.d.mts +5 -0
  43. package/dist/vite.d.ts +5 -0
  44. package/dist/vite.js +1370 -0
  45. package/dist/vite.js.map +1 -0
  46. package/dist/vite.mjs +1366 -0
  47. package/dist/vite.mjs.map +1 -0
  48. package/dist/vue.d.mts +19 -0
  49. package/dist/vue.d.ts +19 -0
  50. package/dist/vue.js +1078 -0
  51. package/dist/vue.js.map +1 -0
  52. package/dist/vue.mjs +1072 -0
  53. package/dist/vue.mjs.map +1 -0
  54. package/dist/webpack.d.mts +1 -1
  55. package/dist/webpack.d.ts +1 -1
  56. package/dist/webpack.js +178 -18
  57. package/dist/webpack.js.map +1 -1
  58. package/dist/webpack.mjs +178 -18
  59. package/dist/webpack.mjs.map +1 -1
  60. package/dist/widget.d.mts +1 -1
  61. package/dist/widget.d.ts +1 -1
  62. package/package.json +48 -2
package/dist/astro.mjs CHANGED
@@ -162,7 +162,7 @@ function detectFramework(projectRoot = process.cwd()) {
162
162
  };
163
163
  }
164
164
  function resolveConfig(config = {}) {
165
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
165
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M;
166
166
  const frameworkInfo = detectFramework();
167
167
  return {
168
168
  title: config.title || "My Site",
@@ -178,26 +178,43 @@ function resolveConfig(config = {}) {
178
178
  rawMarkdown: ((_d = config.generators) == null ? void 0 : _d.rawMarkdown) !== false,
179
179
  manifest: ((_e = config.generators) == null ? void 0 : _e.manifest) !== false,
180
180
  sitemap: ((_f = config.generators) == null ? void 0 : _f.sitemap) !== false,
181
- aiIndex: ((_g = config.generators) == null ? void 0 : _g.aiIndex) !== false
181
+ aiIndex: ((_g = config.generators) == null ? void 0 : _g.aiIndex) !== false,
182
+ schema: ((_h = config.generators) == null ? void 0 : _h.schema) !== false
182
183
  },
183
184
  robots: {
184
- allow: ((_h = config.robots) == null ? void 0 : _h.allow) || ["/"],
185
- disallow: ((_i = config.robots) == null ? void 0 : _i.disallow) || [],
186
- crawlDelay: ((_j = config.robots) == null ? void 0 : _j.crawlDelay) || 0,
187
- sitemap: ((_k = config.robots) == null ? void 0 : _k.sitemap) || ""
185
+ allow: ((_i = config.robots) == null ? void 0 : _i.allow) || ["/"],
186
+ disallow: ((_j = config.robots) == null ? void 0 : _j.disallow) || [],
187
+ crawlDelay: ((_k = config.robots) == null ? void 0 : _k.crawlDelay) || 0,
188
+ sitemap: ((_l = config.robots) == null ? void 0 : _l.sitemap) || ""
189
+ },
190
+ schema: {
191
+ enabled: ((_m = config.schema) == null ? void 0 : _m.enabled) !== false,
192
+ organization: {
193
+ name: ((_o = (_n = config.schema) == null ? void 0 : _n.organization) == null ? void 0 : _o.name) || config.title || "My Site",
194
+ url: ((_q = (_p = config.schema) == null ? void 0 : _p.organization) == null ? void 0 : _q.url) || config.url || "https://example.com",
195
+ logo: ((_s = (_r = config.schema) == null ? void 0 : _r.organization) == null ? void 0 : _s.logo) || "",
196
+ sameAs: ((_u = (_t = config.schema) == null ? void 0 : _t.organization) == null ? void 0 : _u.sameAs) || []
197
+ },
198
+ defaultType: ((_v = config.schema) == null ? void 0 : _v.defaultType) || "WebPage"
199
+ },
200
+ og: {
201
+ enabled: ((_w = config.og) == null ? void 0 : _w.enabled) !== false,
202
+ image: ((_x = config.og) == null ? void 0 : _x.image) || "",
203
+ twitterHandle: ((_y = config.og) == null ? void 0 : _y.twitterHandle) || "",
204
+ type: ((_z = config.og) == null ? void 0 : _z.type) || "website"
188
205
  },
189
206
  widget: {
190
- enabled: ((_l = config.widget) == null ? void 0 : _l.enabled) !== false,
191
- position: ((_m = config.widget) == null ? void 0 : _m.position) || "bottom-right",
207
+ enabled: ((_A = config.widget) == null ? void 0 : _A.enabled) !== false,
208
+ position: ((_B = config.widget) == null ? void 0 : _B.position) || "bottom-right",
192
209
  theme: {
193
- background: ((_o = (_n = config.widget) == null ? void 0 : _n.theme) == null ? void 0 : _o.background) || "rgba(18, 18, 24, 0.9)",
194
- text: ((_q = (_p = config.widget) == null ? void 0 : _p.theme) == null ? void 0 : _q.text) || "#C0C0C5",
195
- accent: ((_s = (_r = config.widget) == null ? void 0 : _r.theme) == null ? void 0 : _s.accent) || "#E8E8EA",
196
- badge: ((_u = (_t = config.widget) == null ? void 0 : _t.theme) == null ? void 0 : _u.badge) || "#4ADE80"
210
+ background: ((_D = (_C = config.widget) == null ? void 0 : _C.theme) == null ? void 0 : _D.background) || "rgba(18, 18, 24, 0.9)",
211
+ text: ((_F = (_E = config.widget) == null ? void 0 : _E.theme) == null ? void 0 : _F.text) || "#C0C0C5",
212
+ accent: ((_H = (_G = config.widget) == null ? void 0 : _G.theme) == null ? void 0 : _H.accent) || "#E8E8EA",
213
+ badge: ((_J = (_I = config.widget) == null ? void 0 : _I.theme) == null ? void 0 : _J.badge) || "#4ADE80"
197
214
  },
198
- humanLabel: ((_v = config.widget) == null ? void 0 : _v.humanLabel) || "Human",
199
- aiLabel: ((_w = config.widget) == null ? void 0 : _w.aiLabel) || "AI",
200
- showBadge: ((_x = config.widget) == null ? void 0 : _x.showBadge) !== false
215
+ humanLabel: ((_K = config.widget) == null ? void 0 : _K.humanLabel) || "Human",
216
+ aiLabel: ((_L = config.widget) == null ? void 0 : _L.aiLabel) || "AI",
217
+ showBadge: ((_M = config.widget) == null ? void 0 : _M.showBadge) !== false
201
218
  }
202
219
  };
203
220
  }
@@ -496,8 +513,8 @@ function generatePageMarkdownFiles(config) {
496
513
  const generated = [];
497
514
  const pages = config.pages || [];
498
515
  for (const page of pages) {
516
+ if (!page.content) continue;
499
517
  const pageTitle = page.title || (page.pathname === "/" ? config.title : void 0);
500
- if (!page.content && !pageTitle) continue;
501
518
  let filename;
502
519
  if (page.pathname === "/") {
503
520
  filename = "index.md";
@@ -767,6 +784,147 @@ function generateAIIndex(config) {
767
784
  };
768
785
  return JSON.stringify(index, null, 2);
769
786
  }
787
+
788
+ // src/core/schema.ts
789
+ function generateSchema(config) {
790
+ const output = generateSchemaObjects(config);
791
+ return JSON.stringify(output, null, 2);
792
+ }
793
+ function generateSchemaObjects(config) {
794
+ const siteSchemas = generateSiteSchemas(config);
795
+ const pageSchemas = {};
796
+ for (const page of config.pages) {
797
+ const schemas = generatePageSchemas(page, config);
798
+ if (schemas.length > 0) {
799
+ pageSchemas[page.pathname] = schemas;
800
+ }
801
+ }
802
+ return { site: siteSchemas, pages: pageSchemas };
803
+ }
804
+ function generateSiteSchemas(config) {
805
+ const schemas = [];
806
+ schemas.push({
807
+ "@context": "https://schema.org",
808
+ "@type": "WebSite",
809
+ name: config.title,
810
+ description: config.description || void 0,
811
+ url: config.url
812
+ });
813
+ const org = config.schema.organization;
814
+ if (org.name || org.sameAs.length > 0) {
815
+ const orgSchema = {
816
+ "@context": "https://schema.org",
817
+ "@type": "Organization",
818
+ name: org.name,
819
+ url: org.url
820
+ };
821
+ if (org.logo) orgSchema.logo = org.logo;
822
+ if (org.sameAs.length > 0) orgSchema.sameAs = org.sameAs;
823
+ schemas.push(orgSchema);
824
+ }
825
+ return schemas;
826
+ }
827
+ function generatePageSchemas(page, config) {
828
+ const schemas = [];
829
+ const pageUrl = page.pathname === "/" ? config.url : `${config.url.replace(/\/$/, "")}${page.pathname}`;
830
+ const faqItems = detectFaqPatterns(page.content || "");
831
+ if (faqItems.length > 0) {
832
+ schemas.push({
833
+ "@context": "https://schema.org",
834
+ "@type": "FAQPage",
835
+ mainEntity: faqItems.map(({ question, answer }) => ({
836
+ "@type": "Question",
837
+ name: question,
838
+ acceptedAnswer: {
839
+ "@type": "Answer",
840
+ text: answer
841
+ }
842
+ }))
843
+ });
844
+ }
845
+ const pageType = config.schema.defaultType;
846
+ const pageSchema = {
847
+ "@context": "https://schema.org",
848
+ "@type": pageType,
849
+ name: page.title || config.title,
850
+ url: pageUrl
851
+ };
852
+ if (page.description) pageSchema.description = page.description;
853
+ if (pageType === "Article") {
854
+ pageSchema.headline = page.title || config.title;
855
+ pageSchema.author = {
856
+ "@type": "Organization",
857
+ name: config.schema.organization.name
858
+ };
859
+ }
860
+ schemas.push(pageSchema);
861
+ if (page.pathname !== "/") {
862
+ const breadcrumbs = generateBreadcrumbs(page.pathname, config);
863
+ if (breadcrumbs.length > 1) {
864
+ schemas.push({
865
+ "@context": "https://schema.org",
866
+ "@type": "BreadcrumbList",
867
+ itemListElement: breadcrumbs.map((crumb, i) => ({
868
+ "@type": "ListItem",
869
+ position: i + 1,
870
+ name: crumb.name,
871
+ item: crumb.url
872
+ }))
873
+ });
874
+ }
875
+ }
876
+ return schemas;
877
+ }
878
+ function generateBreadcrumbs(pathname, config) {
879
+ const baseUrl = config.url.replace(/\/$/, "");
880
+ const parts = pathname.split("/").filter(Boolean);
881
+ const crumbs = [
882
+ { name: "Home", url: baseUrl + "/" }
883
+ ];
884
+ let currentPath = "";
885
+ for (const part of parts) {
886
+ currentPath += "/" + part;
887
+ crumbs.push({
888
+ name: part.charAt(0).toUpperCase() + part.slice(1).replace(/-/g, " "),
889
+ url: baseUrl + currentPath
890
+ });
891
+ }
892
+ return crumbs;
893
+ }
894
+ function detectFaqPatterns(content) {
895
+ const items = [];
896
+ const lines = content.split("\n");
897
+ for (let i = 0; i < lines.length; i++) {
898
+ const line = lines[i].trim();
899
+ const headingMatch = line.match(/^#{1,6}\s+(.+\?)\s*$/);
900
+ if (headingMatch) {
901
+ const answerLines = [];
902
+ for (let j = i + 1; j < lines.length; j++) {
903
+ const nextLine = lines[j].trim();
904
+ if (!nextLine) {
905
+ if (answerLines.length > 0) break;
906
+ continue;
907
+ }
908
+ if (/^#{1,6}\s/.test(nextLine)) break;
909
+ answerLines.push(nextLine);
910
+ }
911
+ if (answerLines.length > 0) {
912
+ items.push({
913
+ question: headingMatch[1],
914
+ answer: answerLines.join(" ").slice(0, 500)
915
+ });
916
+ }
917
+ }
918
+ }
919
+ return items;
920
+ }
921
+ function generateJsonLdScript(schemas) {
922
+ if (schemas.length === 0) return "";
923
+ if (schemas.length === 1) {
924
+ return `<script type="application/ld+json">${JSON.stringify(schemas[0])}</script>`;
925
+ }
926
+ return schemas.map((s) => `<script type="application/ld+json">${JSON.stringify(s)}</script>`).join("\n");
927
+ }
770
928
  async function generateAEOFiles(configOrRoot, maybeConfig) {
771
929
  var _a;
772
930
  let config;
@@ -795,7 +953,7 @@ async function generateAEOFiles(configOrRoot, maybeConfig) {
795
953
  if (config.generators.llmsTxt) {
796
954
  try {
797
955
  const content = generateLlmsTxt(config);
798
- writeFileSync(join(outDir, "llms.txt"), "\uFEFF" + content, "utf-8");
956
+ writeFileSync(join(outDir, "llms.txt"), content, "utf-8");
799
957
  files.push("llms.txt");
800
958
  } catch (e) {
801
959
  errors.push(`llms.txt: ${e.message}`);
@@ -804,7 +962,7 @@ async function generateAEOFiles(configOrRoot, maybeConfig) {
804
962
  if (config.generators.llmsFullTxt) {
805
963
  try {
806
964
  const content = generateLlmsFullTxt(config);
807
- writeFileSync(join(outDir, "llms-full.txt"), "\uFEFF" + content, "utf-8");
965
+ writeFileSync(join(outDir, "llms-full.txt"), content, "utf-8");
808
966
  files.push("llms-full.txt");
809
967
  } catch (e) {
810
968
  errors.push(`llms-full.txt: ${e.message}`);
@@ -855,12 +1013,131 @@ async function generateAEOFiles(configOrRoot, maybeConfig) {
855
1013
  errors.push(`ai-index.json: ${e.message}`);
856
1014
  }
857
1015
  }
1016
+ if (config.generators.schema && config.schema.enabled) {
1017
+ try {
1018
+ const content = generateSchema(config);
1019
+ writeFileSync(join(outDir, "schema.json"), content, "utf-8");
1020
+ files.push("schema.json");
1021
+ } catch (e) {
1022
+ errors.push(`schema.json: ${e.message}`);
1023
+ }
1024
+ }
858
1025
  return { files, errors };
859
1026
  }
1027
+
1028
+ // src/core/html-extract.ts
1029
+ function extractTextFromHtml(html) {
1030
+ let text = html;
1031
+ text = text.replace(/<script[\s\S]*?<\/script>/gi, "");
1032
+ text = text.replace(/<style[\s\S]*?<\/style>/gi, "");
1033
+ text = text.replace(/<svg[\s\S]*?<\/svg>/gi, "");
1034
+ const mainMatch = text.match(/<main[^>]*>([\s\S]*)<\/main>/i);
1035
+ if (mainMatch) {
1036
+ text = mainMatch[1];
1037
+ } else {
1038
+ text = text.replace(/<nav[\s\S]*?<\/nav>/gi, "");
1039
+ text = text.replace(/<header[\s\S]*?<\/header>/gi, "");
1040
+ text = text.replace(/<footer[\s\S]*?<\/footer>/gi, "");
1041
+ }
1042
+ text = text.replace(/<a[^>]+href=["']([^"']*)["'][^>]*>([\s\S]*?)<\/a>/gi, (_, url, inner) => {
1043
+ if (/<(?:h[1-6]|div|p|section)[^>]*>/i.test(inner)) {
1044
+ const cleanInner = inner.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
1045
+ return `
1046
+ [${cleanInner.slice(0, 120).trim()}](${url})
1047
+ `;
1048
+ }
1049
+ return `[${inner}](${url})`;
1050
+ });
1051
+ text = text.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, "\n\n## $1\n\n");
1052
+ text = text.replace(/<h2[^>]*>([\s\S]*?)<\/h2>/gi, "\n\n## $1\n\n");
1053
+ text = text.replace(/<h3[^>]*>([\s\S]*?)<\/h3>/gi, "\n\n### $1\n\n");
1054
+ text = text.replace(/<h4[^>]*>([\s\S]*?)<\/h4>/gi, "\n\n#### $1\n\n");
1055
+ text = text.replace(/<h5[^>]*>([\s\S]*?)<\/h5>/gi, "\n\n##### $1\n\n");
1056
+ text = text.replace(/<h6[^>]*>([\s\S]*?)<\/h6>/gi, "\n\n###### $1\n\n");
1057
+ text = text.replace(/<a[^>]+href=["']([^"']*)["'][^>]*>([\s\S]*?)<\/a>/gi, "[$2]($1)");
1058
+ text = text.replace(/<(?:strong|b)[^>]*>([\s\S]*?)<\/(?:strong|b)>/gi, "**$1**");
1059
+ text = text.replace(/<(?:em|i)[^>]*>([\s\S]*?)<\/(?:em|i)>/gi, "*$1*");
1060
+ text = text.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, "\n- $1");
1061
+ text = text.replace(/<blockquote[^>]*>([\s\S]*?)<\/blockquote>/gi, "\n\n> $1\n\n");
1062
+ text = text.replace(/<hr[^>]*\/?>/gi, "\n\n---\n\n");
1063
+ text = text.replace(/<br[^>]*\/?>/gi, "\n");
1064
+ text = text.replace(/<\/p>/gi, "\n\n");
1065
+ text = text.replace(/<p[^>]*>/gi, "");
1066
+ text = text.replace(/<\/?(?:div|section|article|header|main|aside|figure|figcaption|table|thead|tbody|tr|td|th|ul|ol|dl|dt|dd)[^>]*>/gi, "\n");
1067
+ text = text.replace(/<[^>]+>/g, "");
1068
+ text = text.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&nbsp;/g, " ").replace(/&copy;/g, "(c)");
1069
+ text = text.replace(/[\u{1F1E0}-\u{1FAFF}\u{2600}-\u{27BF}\u{FE00}-\u{FE0F}\u{200D}\u{20E3}]/gu, "");
1070
+ text = text.split("\n").map((l) => l.replace(/\s+/g, " ").trim()).join("\n");
1071
+ text = text.replace(/\n{3,}/g, "\n\n");
1072
+ text = text.replace(/\[[\s\n]+/g, "[").replace(/[\s\n]+\]/g, "]");
1073
+ text = text.replace(/(#{2,6})\s*\n+\s*/g, "$1 ");
1074
+ text = text.replace(/^#{2,6}\s*$/gm, "");
1075
+ text = text.replace(/\n{3,}/g, "\n\n");
1076
+ return text.trim().slice(0, 8e3);
1077
+ }
1078
+ function extractTitle2(html) {
1079
+ var _a, _b;
1080
+ const match = html.match(/<title>([^<]*)<\/title>/i);
1081
+ if (!match) return void 0;
1082
+ return ((_b = (_a = match[1]) == null ? void 0 : _a.split("|")[0]) == null ? void 0 : _b.trim()) || match[1];
1083
+ }
1084
+ function extractDescription(html) {
1085
+ const match = html.match(/<meta\s+name=["']description["']\s+content=["']([^"']*)["']/i);
1086
+ return match == null ? void 0 : match[1];
1087
+ }
1088
+ function htmlToMarkdown(html, pagePath, config) {
1089
+ const rawTitle = extractTitle2(html);
1090
+ const description = extractDescription(html);
1091
+ const textContent = extractTextFromHtml(html);
1092
+ const pageUrl = pagePath === "/" ? config.url : `${config.url.replace(/\/$/, "")}${pagePath}`;
1093
+ const lines = [];
1094
+ lines.push("---");
1095
+ if (rawTitle) lines.push(`title: "${rawTitle}"`);
1096
+ if (description) lines.push(`description: "${description}"`);
1097
+ lines.push(`url: ${pageUrl}`);
1098
+ lines.push(`source: ${pageUrl}`);
1099
+ lines.push(`generated_by: aeo.js`);
1100
+ lines.push("---", "");
1101
+ if (rawTitle) lines.push(`# ${rawTitle}`, "");
1102
+ if (description) lines.push(`${description}`, "");
1103
+ if (textContent) lines.push(textContent);
1104
+ return lines.join("\n");
1105
+ }
1106
+
1107
+ // src/core/opengraph.ts
1108
+ function generateOGTags(page, config) {
1109
+ const tags = [];
1110
+ const pageUrl = page.pathname === "/" ? config.url : `${config.url.replace(/\/$/, "")}${page.pathname}`;
1111
+ const title = page.title || config.title;
1112
+ const description = page.description || config.description;
1113
+ tags.push({ property: "og:type", content: config.og.type });
1114
+ tags.push({ property: "og:title", content: title });
1115
+ if (description) tags.push({ property: "og:description", content: description });
1116
+ tags.push({ property: "og:url", content: pageUrl });
1117
+ tags.push({ property: "og:site_name", content: config.title });
1118
+ if (config.og.image) tags.push({ property: "og:image", content: config.og.image });
1119
+ tags.push({ name: "twitter:card", content: config.og.image ? "summary_large_image" : "summary" });
1120
+ tags.push({ name: "twitter:title", content: title });
1121
+ if (description) tags.push({ name: "twitter:description", content: description });
1122
+ if (config.og.twitterHandle) tags.push({ name: "twitter:site", content: config.og.twitterHandle });
1123
+ if (config.og.image) tags.push({ name: "twitter:image", content: config.og.image });
1124
+ return tags;
1125
+ }
1126
+ function generateOGTagsHtml(page, config) {
1127
+ const tags = generateOGTags(page, config);
1128
+ return tags.map((tag) => {
1129
+ if (tag.property) return `<meta property="${tag.property}" content="${escapeAttr(tag.content)}" />`;
1130
+ return `<meta name="${tag.name}" content="${escapeAttr(tag.content)}" />`;
1131
+ }).join("\n ");
1132
+ }
1133
+ function escapeAttr(str) {
1134
+ return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1135
+ }
1136
+
1137
+ // src/plugins/astro.ts
860
1138
  function scanBuiltPages(dir, baseUrl) {
861
1139
  const pages = [];
862
1140
  function walk(currentDir) {
863
- var _a;
864
1141
  try {
865
1142
  const entries = readdirSync(currentDir);
866
1143
  for (const entry of entries) {
@@ -871,8 +1148,8 @@ function scanBuiltPages(dir, baseUrl) {
871
1148
  } else if (entry === "index.html" || entry.endsWith(".html") && entry !== "404.html" && entry !== "500.html") {
872
1149
  try {
873
1150
  const html = readFileSync(fullPath, "utf-8");
874
- const titleMatch = html.match(/<title>([^<]*)<\/title>/i);
875
- const descMatch = html.match(/<meta\s+name=["']description["']\s+content=["']([^"']*)["']/i);
1151
+ const title = extractTitle2(html);
1152
+ const description = extractDescription(html);
876
1153
  const textContent = extractTextFromHtml(html);
877
1154
  let pathname;
878
1155
  const relative8 = fullPath.slice(dir.length);
@@ -883,12 +1160,10 @@ function scanBuiltPages(dir, baseUrl) {
883
1160
  pathname = "/" + relative8.replace(/\.html$/, "");
884
1161
  }
885
1162
  pathname = pathname.replace(/\/+/g, "/") || "/";
886
- const rawTitle = titleMatch ? titleMatch[1] : void 0;
887
- const title = ((_a = rawTitle == null ? void 0 : rawTitle.split("|")[0]) == null ? void 0 : _a.trim()) || rawTitle;
888
1163
  pages.push({
889
1164
  pathname,
890
1165
  title,
891
- description: descMatch ? descMatch[1] : void 0,
1166
+ description,
892
1167
  content: textContent
893
1168
  });
894
1169
  } catch {
@@ -933,76 +1208,6 @@ function scanDevPages(pagesDir) {
933
1208
  }
934
1209
  return pages;
935
1210
  }
936
- function extractTextFromHtml(html) {
937
- let text = html;
938
- text = text.replace(/<script[\s\S]*?<\/script>/gi, "");
939
- text = text.replace(/<style[\s\S]*?<\/style>/gi, "");
940
- text = text.replace(/<svg[\s\S]*?<\/svg>/gi, "");
941
- const mainMatch = text.match(/<main[^>]*>([\s\S]*)<\/main>/i);
942
- if (mainMatch) {
943
- text = mainMatch[1];
944
- } else {
945
- text = text.replace(/<nav[\s\S]*?<\/nav>/gi, "");
946
- text = text.replace(/<header[\s\S]*?<\/header>/gi, "");
947
- text = text.replace(/<footer[\s\S]*?<\/footer>/gi, "");
948
- }
949
- text = text.replace(/<a[^>]+href=["']([^"']*)["'][^>]*>([\s\S]*?)<\/a>/gi, (_, url, inner) => {
950
- if (/<(?:h[1-6]|div|p|section)[^>]*>/i.test(inner)) {
951
- const cleanInner = inner.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
952
- return `
953
- [${cleanInner.slice(0, 120).trim()}](${url})
954
- `;
955
- }
956
- return `[${inner}](${url})`;
957
- });
958
- text = text.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, "\n\n## $1\n\n");
959
- text = text.replace(/<h2[^>]*>([\s\S]*?)<\/h2>/gi, "\n\n## $1\n\n");
960
- text = text.replace(/<h3[^>]*>([\s\S]*?)<\/h3>/gi, "\n\n### $1\n\n");
961
- text = text.replace(/<h4[^>]*>([\s\S]*?)<\/h4>/gi, "\n\n#### $1\n\n");
962
- text = text.replace(/<h5[^>]*>([\s\S]*?)<\/h5>/gi, "\n\n##### $1\n\n");
963
- text = text.replace(/<h6[^>]*>([\s\S]*?)<\/h6>/gi, "\n\n###### $1\n\n");
964
- text = text.replace(/<a[^>]+href=["']([^"']*)["'][^>]*>([\s\S]*?)<\/a>/gi, "[$2]($1)");
965
- text = text.replace(/<(?:strong|b)[^>]*>([\s\S]*?)<\/(?:strong|b)>/gi, "**$1**");
966
- text = text.replace(/<(?:em|i)[^>]*>([\s\S]*?)<\/(?:em|i)>/gi, "*$1*");
967
- text = text.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, "\n- $1");
968
- text = text.replace(/<blockquote[^>]*>([\s\S]*?)<\/blockquote>/gi, "\n\n> $1\n\n");
969
- text = text.replace(/<hr[^>]*\/?>/gi, "\n\n---\n\n");
970
- text = text.replace(/<br[^>]*\/?>/gi, "\n");
971
- text = text.replace(/<\/p>/gi, "\n\n");
972
- text = text.replace(/<p[^>]*>/gi, "");
973
- text = text.replace(/<\/?(?:div|section|article|header|main|aside|figure|figcaption|table|thead|tbody|tr|td|th|ul|ol|dl|dt|dd)[^>]*>/gi, "\n");
974
- text = text.replace(/<[^>]+>/g, "");
975
- text = text.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&nbsp;/g, " ").replace(/&copy;/g, "(c)");
976
- text = text.replace(/[\u{1F1E0}-\u{1FAFF}\u{2600}-\u{27BF}\u{FE00}-\u{FE0F}\u{200D}\u{20E3}]/gu, "");
977
- text = text.split("\n").map((l) => l.replace(/\s+/g, " ").trim()).join("\n");
978
- text = text.replace(/\n{3,}/g, "\n\n");
979
- text = text.replace(/\[[\s\n]+/g, "[").replace(/[\s\n]+\]/g, "]");
980
- text = text.replace(/(#{2,6})\s*\n+\s*/g, "$1 ");
981
- text = text.replace(/^#{2,6}\s*$/gm, "");
982
- text = text.replace(/\n{3,}/g, "\n\n");
983
- return text.trim().slice(0, 8e3);
984
- }
985
- function htmlToMarkdown(html, pagePath, config) {
986
- var _a, _b;
987
- const titleMatch = html.match(/<title>([^<]*)<\/title>/i);
988
- const descMatch = html.match(/<meta\s+name=["']description["']\s+content=["']([^"']*)["']/i);
989
- const textContent = extractTextFromHtml(html);
990
- const rawTitle = titleMatch ? (_b = (_a = titleMatch[1]) == null ? void 0 : _a.split("|")[0]) == null ? void 0 : _b.trim() : void 0;
991
- const description = descMatch == null ? void 0 : descMatch[1];
992
- const pageUrl = pagePath === "/" ? config.url : `${config.url.replace(/\/$/, "")}${pagePath}`;
993
- const lines = [];
994
- lines.push("---");
995
- if (rawTitle) lines.push(`title: "${rawTitle}"`);
996
- if (description) lines.push(`description: "${description}"`);
997
- lines.push(`url: ${pageUrl}`);
998
- lines.push(`source: ${pageUrl}`);
999
- lines.push(`generated_by: aeo.js`);
1000
- lines.push("---", "");
1001
- if (rawTitle) lines.push(`# ${rawTitle}`, "");
1002
- if (description) lines.push(`${description}`, "");
1003
- if (textContent) lines.push(textContent);
1004
- return lines.join("\n");
1005
- }
1006
1211
  function aeoAstroIntegration(options = {}) {
1007
1212
  let resolvedConfig = resolveConfig(options);
1008
1213
  let astroConfig;
@@ -1023,14 +1228,33 @@ function aeoAstroIntegration(options = {}) {
1023
1228
  }
1024
1229
  }
1025
1230
  if (resolvedConfig.widget.enabled && injectScript) {
1026
- const widgetOpts = JSON.stringify(resolvedConfig.widget);
1231
+ const widgetConfig = JSON.stringify({
1232
+ title: resolvedConfig.title,
1233
+ description: resolvedConfig.description,
1234
+ url: resolvedConfig.url,
1235
+ widget: resolvedConfig.widget
1236
+ });
1027
1237
  injectScript(
1028
1238
  "page",
1029
1239
  `import { AeoWidget } from 'aeo.js/widget';
1030
- if (document.readyState === 'loading') {
1031
- document.addEventListener('DOMContentLoaded', () => { new AeoWidget(${widgetOpts}); });
1032
- } else {
1033
- new AeoWidget(${widgetOpts});
1240
+ let __aeoWidget;
1241
+ function __initAeoWidget() {
1242
+ if (__aeoWidget) __aeoWidget.destroy();
1243
+ try {
1244
+ __aeoWidget = new AeoWidget({ config: ${widgetConfig} });
1245
+ } catch (e) {
1246
+ console.warn('[aeo.js] Widget initialization failed:', e);
1247
+ }
1248
+ }
1249
+ // astro:page-load fires on initial load AND after every View Transition navigation
1250
+ document.addEventListener('astro:page-load', __initAeoWidget);
1251
+ // Fallback for Astro sites without View Transitions
1252
+ if (!document.querySelector('meta[name="astro-view-transitions-enabled"]')) {
1253
+ if (document.readyState === 'loading') {
1254
+ document.addEventListener('DOMContentLoaded', __initAeoWidget);
1255
+ } else {
1256
+ __initAeoWidget();
1257
+ }
1034
1258
  }`
1035
1259
  );
1036
1260
  }
@@ -1150,8 +1374,24 @@ if (document.readyState === 'loading') {
1150
1374
  }
1151
1375
  };
1152
1376
  }
1153
- var AeoMetaTags = ({ config }) => {
1377
+ var AeoMetaTags = ({ config, page }) => {
1154
1378
  const resolvedConfig = resolveConfig(config);
1379
+ const currentPage = page || { pathname: "/" };
1380
+ const pageEntry = {
1381
+ pathname: currentPage.pathname || "/",
1382
+ title: currentPage.title,
1383
+ description: currentPage.description,
1384
+ content: currentPage.content
1385
+ };
1386
+ let jsonLd = "";
1387
+ if (resolvedConfig.schema.enabled) {
1388
+ const schemas = generatePageSchemas(pageEntry, resolvedConfig);
1389
+ jsonLd = generateJsonLdScript(schemas);
1390
+ }
1391
+ let ogTags = "";
1392
+ if (resolvedConfig.og.enabled) {
1393
+ ogTags = generateOGTagsHtml(pageEntry, resolvedConfig);
1394
+ }
1155
1395
  return `
1156
1396
  <link rel="alternate" type="text/plain" href="/llms.txt" title="LLM Summary" />
1157
1397
  <link rel="alternate" type="text/plain" href="/llms-full.txt" title="Full Content for LLMs" />
@@ -1160,6 +1400,8 @@ var AeoMetaTags = ({ config }) => {
1160
1400
  <meta name="aeo:title" content="${resolvedConfig.title}" />
1161
1401
  <meta name="aeo:description" content="${resolvedConfig.description}" />
1162
1402
  <meta name="aeo:url" content="${resolvedConfig.url}" />
1403
+ ${ogTags}
1404
+ ${jsonLd}
1163
1405
  `;
1164
1406
  };
1165
1407
  function defineAeoConfig(config) {