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