nuxt-ai-ready 1.2.0 β†’ 1.3.0

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 (40) hide show
  1. package/README.md +2 -0
  2. package/dist/devtools/200.html +1 -1
  3. package/dist/devtools/404.html +1 -1
  4. package/dist/devtools/_nuxt/builds/latest.json +1 -1
  5. package/dist/devtools/_nuxt/builds/meta/cfed7300-fd87-4d26-98a1-6e1d8a608160.json +1 -0
  6. package/dist/devtools/debug/index.html +1 -1
  7. package/dist/devtools/docs/index.html +1 -1
  8. package/dist/devtools/index.html +1 -1
  9. package/dist/devtools/llms-txt/index.html +1 -1
  10. package/dist/devtools/pages/index.html +1 -1
  11. package/dist/module.d.mts +11 -0
  12. package/dist/module.json +1 -1
  13. package/dist/module.mjs +118 -12
  14. package/dist/runtime/llms-txt-utils.js +37 -4
  15. package/dist/runtime/server/db/drizzle/queries.d.ts +3 -1
  16. package/dist/runtime/server/db/drizzle/queries.js +71 -11
  17. package/dist/runtime/server/db/queries.d.ts +7 -1
  18. package/dist/runtime/server/db/queries.js +33 -14
  19. package/dist/runtime/server/db/schema/postgres.d.ts +34 -0
  20. package/dist/runtime/server/db/schema/postgres.js +4 -2
  21. package/dist/runtime/server/db/schema/sqlite.d.ts +38 -0
  22. package/dist/runtime/server/db/schema/sqlite.js +4 -2
  23. package/dist/runtime/server/db/schema-sql.d.ts +18 -2
  24. package/dist/runtime/server/db/schema-sql.js +100 -85
  25. package/dist/runtime/server/db/shared.d.ts +13 -3
  26. package/dist/runtime/server/db/shared.js +31 -14
  27. package/dist/runtime/server/middleware/markdown.d.ts +1 -1
  28. package/dist/runtime/server/middleware/markdown.js +64 -28
  29. package/dist/runtime/server/middleware/markdown.prerender.js +37 -1
  30. package/dist/runtime/server/utils/content.d.ts +8 -0
  31. package/dist/runtime/server/utils/content.js +4 -0
  32. package/dist/runtime/server/utils/frontmatter.d.ts +6 -0
  33. package/dist/runtime/server/utils/frontmatter.js +7 -0
  34. package/dist/runtime/server/utils/i18n.d.ts +32 -0
  35. package/dist/runtime/server/utils/i18n.js +33 -0
  36. package/dist/runtime/server/utils.d.ts +1 -0
  37. package/dist/runtime/server/utils.js +5 -0
  38. package/dist/runtime/types.d.ts +13 -0
  39. package/package.json +1 -1
  40. package/dist/devtools/_nuxt/builds/meta/feeb28d1-669d-4cde-b5e6-08a29aab779a.json +0 -1
package/README.md CHANGED
@@ -18,11 +18,13 @@ Nuxt AI Ready implements both. It converts your pages to markdown, generates llm
18
18
 
19
19
  - πŸ“„ **llms.txt Generation**: Auto-generate `llms.txt` and `llms-full.txt` with page metadata and full markdown content
20
20
  - πŸš€ **On-Demand Markdown**: Any route available as `.md` (e.g., `/about` β†’ `/about.md`), automatically served to AI crawlers
21
+ - πŸ“ **[Nuxt Content](https://content.nuxt.com) Integration**: Routes backed by a page collection serve source markdown directly, skipping HTMLβ†’markdown conversion
21
22
  - πŸ“‘ **Content Signals**: Configure AI training/search/input permissions via [Nuxt Robots](https://nuxtseo.com/robots)
22
23
  - 🌐 **Sitemap Integration**: Index AI-allowed pages via [Nuxt Sitemap](https://nuxtseo.com/sitemap)
23
24
  - ⚑ **MCP Server**: `list_pages` and `search_pages` tools with FTS5 full-text search
24
25
  - πŸ—„οΈ **Runtime Indexing**: Index pages on-demand without prerendering, with SQLite/D1/LibSQL support
25
26
  - πŸ”” **[IndexNow](https://nuxtseo.com/ai-ready/guides/indexnow)**: Instantly notify Bing, Yandex, and other search engines when pages change
27
+ - 🌏 **[i18n Aware](https://nuxtseo.com/ai-ready/guides/i18n)**: Auto-detects [`@nuxtjs/i18n`](https://i18n.nuxtjs.org/) for hreflang `Link` headers, locale-tagged frontmatter, and an Available Languages section in `llms.txt`
26
28
  - 🧠 **[RAG Ready](https://nuxtseo.com/ai-ready/advanced/rag-example)**: Markdown output optimized for vectorizing and semantic search
27
29
 
28
30
  ## Installation
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__nuxt-ai-ready/_nuxt/entry.BLpi4vfB.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js"><script type="module" src="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js" crossorigin></script><script>"use strict";(()=>{const t=window,e=document.documentElement,c=["dark","light"],n=getStorageValue("localStorage","nuxt-color-mode")||"system";let i=n==="system"?u():n;const r=e.getAttribute("data-color-mode-forced");r&&(i=r),l(i),t["__NUXT_COLOR_MODE__"]={preference:n,value:i,getColorScheme:u,addColorScheme:l,removeColorScheme:d};function l(o){const s=""+o+"",a="";e.classList?e.classList.add(s):e.className+=" "+s,a&&e.setAttribute("data-"+a,o)}function d(o){const s=""+o+"",a="";e.classList?e.classList.remove(s):e.className=e.className.replace(new RegExp(s,"g"),""),a&&e.removeAttribute("data-"+a)}function f(o){return t.matchMedia("(prefers-color-scheme"+o+")")}function u(){if(t.matchMedia&&f("").media!=="not all"){for(const o of c)if(f(":"+o).matches)return o}return"light"}})();function getStorageValue(t,e){switch(t){case"localStorage":return window.localStorage.getItem(e);case"sessionStorage":return window.sessionStorage.getItem(e);case"cookie":return getCookie(e);default:return null}}function getCookie(t){const c=("; "+window.document.cookie).split("; "+t+"=");if(c.length===2)return c.pop()?.split(";").shift()}</script></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-ai-ready",buildId:"feeb28d1-669d-4cde-b5e6-08a29aab779a",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1776955857789,false]</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__nuxt-ai-ready/_nuxt/entry.BLpi4vfB.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js"><script type="module" src="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js" crossorigin></script><script>"use strict";(()=>{const t=window,e=document.documentElement,c=["dark","light"],n=getStorageValue("localStorage","nuxt-color-mode")||"system";let i=n==="system"?u():n;const r=e.getAttribute("data-color-mode-forced");r&&(i=r),l(i),t["__NUXT_COLOR_MODE__"]={preference:n,value:i,getColorScheme:u,addColorScheme:l,removeColorScheme:d};function l(o){const s=""+o+"",a="";e.classList?e.classList.add(s):e.className+=" "+s,a&&e.setAttribute("data-"+a,o)}function d(o){const s=""+o+"",a="";e.classList?e.classList.remove(s):e.className=e.className.replace(new RegExp(s,"g"),""),a&&e.removeAttribute("data-"+a)}function f(o){return t.matchMedia("(prefers-color-scheme"+o+")")}function u(){if(t.matchMedia&&f("").media!=="not all"){for(const o of c)if(f(":"+o).matches)return o}return"light"}})();function getStorageValue(t,e){switch(t){case"localStorage":return window.localStorage.getItem(e);case"sessionStorage":return window.sessionStorage.getItem(e);case"cookie":return getCookie(e);default:return null}}function getCookie(t){const c=("; "+window.document.cookie).split("; "+t+"=");if(c.length===2)return c.pop()?.split(";").shift()}</script></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-ai-ready",buildId:"cfed7300-fd87-4d26-98a1-6e1d8a608160",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1777105835626,false]</script></body></html>
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__nuxt-ai-ready/_nuxt/entry.BLpi4vfB.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js"><script type="module" src="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js" crossorigin></script><script>"use strict";(()=>{const t=window,e=document.documentElement,c=["dark","light"],n=getStorageValue("localStorage","nuxt-color-mode")||"system";let i=n==="system"?u():n;const r=e.getAttribute("data-color-mode-forced");r&&(i=r),l(i),t["__NUXT_COLOR_MODE__"]={preference:n,value:i,getColorScheme:u,addColorScheme:l,removeColorScheme:d};function l(o){const s=""+o+"",a="";e.classList?e.classList.add(s):e.className+=" "+s,a&&e.setAttribute("data-"+a,o)}function d(o){const s=""+o+"",a="";e.classList?e.classList.remove(s):e.className=e.className.replace(new RegExp(s,"g"),""),a&&e.removeAttribute("data-"+a)}function f(o){return t.matchMedia("(prefers-color-scheme"+o+")")}function u(){if(t.matchMedia&&f("").media!=="not all"){for(const o of c)if(f(":"+o).matches)return o}return"light"}})();function getStorageValue(t,e){switch(t){case"localStorage":return window.localStorage.getItem(e);case"sessionStorage":return window.sessionStorage.getItem(e);case"cookie":return getCookie(e);default:return null}}function getCookie(t){const c=("; "+window.document.cookie).split("; "+t+"=");if(c.length===2)return c.pop()?.split(";").shift()}</script></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-ai-ready",buildId:"feeb28d1-669d-4cde-b5e6-08a29aab779a",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1776955857789,false]</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__nuxt-ai-ready/_nuxt/entry.BLpi4vfB.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js"><script type="module" src="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js" crossorigin></script><script>"use strict";(()=>{const t=window,e=document.documentElement,c=["dark","light"],n=getStorageValue("localStorage","nuxt-color-mode")||"system";let i=n==="system"?u():n;const r=e.getAttribute("data-color-mode-forced");r&&(i=r),l(i),t["__NUXT_COLOR_MODE__"]={preference:n,value:i,getColorScheme:u,addColorScheme:l,removeColorScheme:d};function l(o){const s=""+o+"",a="";e.classList?e.classList.add(s):e.className+=" "+s,a&&e.setAttribute("data-"+a,o)}function d(o){const s=""+o+"",a="";e.classList?e.classList.remove(s):e.className=e.className.replace(new RegExp(s,"g"),""),a&&e.removeAttribute("data-"+a)}function f(o){return t.matchMedia("(prefers-color-scheme"+o+")")}function u(){if(t.matchMedia&&f("").media!=="not all"){for(const o of c)if(f(":"+o).matches)return o}return"light"}})();function getStorageValue(t,e){switch(t){case"localStorage":return window.localStorage.getItem(e);case"sessionStorage":return window.sessionStorage.getItem(e);case"cookie":return getCookie(e);default:return null}}function getCookie(t){const c=("; "+window.document.cookie).split("; "+t+"=");if(c.length===2)return c.pop()?.split(";").shift()}</script></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-ai-ready",buildId:"cfed7300-fd87-4d26-98a1-6e1d8a608160",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1777105835626,false]</script></body></html>
@@ -1 +1 @@
1
- {"id":"feeb28d1-669d-4cde-b5e6-08a29aab779a","timestamp":1776955848781}
1
+ {"id":"cfed7300-fd87-4d26-98a1-6e1d8a608160","timestamp":1777105827246}
@@ -0,0 +1 @@
1
+ {"id":"cfed7300-fd87-4d26-98a1-6e1d8a608160","timestamp":1777105827246,"prerendered":[]}
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__nuxt-ai-ready/_nuxt/entry.BLpi4vfB.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js"><script type="module" src="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js" crossorigin></script><script>"use strict";(()=>{const t=window,e=document.documentElement,c=["dark","light"],n=getStorageValue("localStorage","nuxt-color-mode")||"system";let i=n==="system"?u():n;const r=e.getAttribute("data-color-mode-forced");r&&(i=r),l(i),t["__NUXT_COLOR_MODE__"]={preference:n,value:i,getColorScheme:u,addColorScheme:l,removeColorScheme:d};function l(o){const s=""+o+"",a="";e.classList?e.classList.add(s):e.className+=" "+s,a&&e.setAttribute("data-"+a,o)}function d(o){const s=""+o+"",a="";e.classList?e.classList.remove(s):e.className=e.className.replace(new RegExp(s,"g"),""),a&&e.removeAttribute("data-"+a)}function f(o){return t.matchMedia("(prefers-color-scheme"+o+")")}function u(){if(t.matchMedia&&f("").media!=="not all"){for(const o of c)if(f(":"+o).matches)return o}return"light"}})();function getStorageValue(t,e){switch(t){case"localStorage":return window.localStorage.getItem(e);case"sessionStorage":return window.sessionStorage.getItem(e);case"cookie":return getCookie(e);default:return null}}function getCookie(t){const c=("; "+window.document.cookie).split("; "+t+"=");if(c.length===2)return c.pop()?.split(";").shift()}</script></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-ai-ready",buildId:"feeb28d1-669d-4cde-b5e6-08a29aab779a",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1776955857787,false]</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__nuxt-ai-ready/_nuxt/entry.BLpi4vfB.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js"><script type="module" src="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js" crossorigin></script><script>"use strict";(()=>{const t=window,e=document.documentElement,c=["dark","light"],n=getStorageValue("localStorage","nuxt-color-mode")||"system";let i=n==="system"?u():n;const r=e.getAttribute("data-color-mode-forced");r&&(i=r),l(i),t["__NUXT_COLOR_MODE__"]={preference:n,value:i,getColorScheme:u,addColorScheme:l,removeColorScheme:d};function l(o){const s=""+o+"",a="";e.classList?e.classList.add(s):e.className+=" "+s,a&&e.setAttribute("data-"+a,o)}function d(o){const s=""+o+"",a="";e.classList?e.classList.remove(s):e.className=e.className.replace(new RegExp(s,"g"),""),a&&e.removeAttribute("data-"+a)}function f(o){return t.matchMedia("(prefers-color-scheme"+o+")")}function u(){if(t.matchMedia&&f("").media!=="not all"){for(const o of c)if(f(":"+o).matches)return o}return"light"}})();function getStorageValue(t,e){switch(t){case"localStorage":return window.localStorage.getItem(e);case"sessionStorage":return window.sessionStorage.getItem(e);case"cookie":return getCookie(e);default:return null}}function getCookie(t){const c=("; "+window.document.cookie).split("; "+t+"=");if(c.length===2)return c.pop()?.split(";").shift()}</script></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-ai-ready",buildId:"cfed7300-fd87-4d26-98a1-6e1d8a608160",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1777105835625,false]</script></body></html>
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__nuxt-ai-ready/_nuxt/entry.BLpi4vfB.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js"><script type="module" src="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js" crossorigin></script><script>"use strict";(()=>{const t=window,e=document.documentElement,c=["dark","light"],n=getStorageValue("localStorage","nuxt-color-mode")||"system";let i=n==="system"?u():n;const r=e.getAttribute("data-color-mode-forced");r&&(i=r),l(i),t["__NUXT_COLOR_MODE__"]={preference:n,value:i,getColorScheme:u,addColorScheme:l,removeColorScheme:d};function l(o){const s=""+o+"",a="";e.classList?e.classList.add(s):e.className+=" "+s,a&&e.setAttribute("data-"+a,o)}function d(o){const s=""+o+"",a="";e.classList?e.classList.remove(s):e.className=e.className.replace(new RegExp(s,"g"),""),a&&e.removeAttribute("data-"+a)}function f(o){return t.matchMedia("(prefers-color-scheme"+o+")")}function u(){if(t.matchMedia&&f("").media!=="not all"){for(const o of c)if(f(":"+o).matches)return o}return"light"}})();function getStorageValue(t,e){switch(t){case"localStorage":return window.localStorage.getItem(e);case"sessionStorage":return window.sessionStorage.getItem(e);case"cookie":return getCookie(e);default:return null}}function getCookie(t){const c=("; "+window.document.cookie).split("; "+t+"=");if(c.length===2)return c.pop()?.split(";").shift()}</script></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-ai-ready",buildId:"feeb28d1-669d-4cde-b5e6-08a29aab779a",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1776955857788,false]</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__nuxt-ai-ready/_nuxt/entry.BLpi4vfB.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js"><script type="module" src="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js" crossorigin></script><script>"use strict";(()=>{const t=window,e=document.documentElement,c=["dark","light"],n=getStorageValue("localStorage","nuxt-color-mode")||"system";let i=n==="system"?u():n;const r=e.getAttribute("data-color-mode-forced");r&&(i=r),l(i),t["__NUXT_COLOR_MODE__"]={preference:n,value:i,getColorScheme:u,addColorScheme:l,removeColorScheme:d};function l(o){const s=""+o+"",a="";e.classList?e.classList.add(s):e.className+=" "+s,a&&e.setAttribute("data-"+a,o)}function d(o){const s=""+o+"",a="";e.classList?e.classList.remove(s):e.className=e.className.replace(new RegExp(s,"g"),""),a&&e.removeAttribute("data-"+a)}function f(o){return t.matchMedia("(prefers-color-scheme"+o+")")}function u(){if(t.matchMedia&&f("").media!=="not all"){for(const o of c)if(f(":"+o).matches)return o}return"light"}})();function getStorageValue(t,e){switch(t){case"localStorage":return window.localStorage.getItem(e);case"sessionStorage":return window.sessionStorage.getItem(e);case"cookie":return getCookie(e);default:return null}}function getCookie(t){const c=("; "+window.document.cookie).split("; "+t+"=");if(c.length===2)return c.pop()?.split(";").shift()}</script></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-ai-ready",buildId:"cfed7300-fd87-4d26-98a1-6e1d8a608160",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1777105835626,false]</script></body></html>
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__nuxt-ai-ready/_nuxt/entry.BLpi4vfB.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js"><script type="module" src="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js" crossorigin></script><script>"use strict";(()=>{const t=window,e=document.documentElement,c=["dark","light"],n=getStorageValue("localStorage","nuxt-color-mode")||"system";let i=n==="system"?u():n;const r=e.getAttribute("data-color-mode-forced");r&&(i=r),l(i),t["__NUXT_COLOR_MODE__"]={preference:n,value:i,getColorScheme:u,addColorScheme:l,removeColorScheme:d};function l(o){const s=""+o+"",a="";e.classList?e.classList.add(s):e.className+=" "+s,a&&e.setAttribute("data-"+a,o)}function d(o){const s=""+o+"",a="";e.classList?e.classList.remove(s):e.className=e.className.replace(new RegExp(s,"g"),""),a&&e.removeAttribute("data-"+a)}function f(o){return t.matchMedia("(prefers-color-scheme"+o+")")}function u(){if(t.matchMedia&&f("").media!=="not all"){for(const o of c)if(f(":"+o).matches)return o}return"light"}})();function getStorageValue(t,e){switch(t){case"localStorage":return window.localStorage.getItem(e);case"sessionStorage":return window.sessionStorage.getItem(e);case"cookie":return getCookie(e);default:return null}}function getCookie(t){const c=("; "+window.document.cookie).split("; "+t+"=");if(c.length===2)return c.pop()?.split(";").shift()}</script></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-ai-ready",buildId:"feeb28d1-669d-4cde-b5e6-08a29aab779a",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1776955857789,false]</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__nuxt-ai-ready/_nuxt/entry.BLpi4vfB.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js"><script type="module" src="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js" crossorigin></script><script>"use strict";(()=>{const t=window,e=document.documentElement,c=["dark","light"],n=getStorageValue("localStorage","nuxt-color-mode")||"system";let i=n==="system"?u():n;const r=e.getAttribute("data-color-mode-forced");r&&(i=r),l(i),t["__NUXT_COLOR_MODE__"]={preference:n,value:i,getColorScheme:u,addColorScheme:l,removeColorScheme:d};function l(o){const s=""+o+"",a="";e.classList?e.classList.add(s):e.className+=" "+s,a&&e.setAttribute("data-"+a,o)}function d(o){const s=""+o+"",a="";e.classList?e.classList.remove(s):e.className=e.className.replace(new RegExp(s,"g"),""),a&&e.removeAttribute("data-"+a)}function f(o){return t.matchMedia("(prefers-color-scheme"+o+")")}function u(){if(t.matchMedia&&f("").media!=="not all"){for(const o of c)if(f(":"+o).matches)return o}return"light"}})();function getStorageValue(t,e){switch(t){case"localStorage":return window.localStorage.getItem(e);case"sessionStorage":return window.sessionStorage.getItem(e);case"cookie":return getCookie(e);default:return null}}function getCookie(t){const c=("; "+window.document.cookie).split("; "+t+"=");if(c.length===2)return c.pop()?.split(";").shift()}</script></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-ai-ready",buildId:"cfed7300-fd87-4d26-98a1-6e1d8a608160",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1777105835627,false]</script></body></html>
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__nuxt-ai-ready/_nuxt/entry.BLpi4vfB.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js"><script type="module" src="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js" crossorigin></script><script>"use strict";(()=>{const t=window,e=document.documentElement,c=["dark","light"],n=getStorageValue("localStorage","nuxt-color-mode")||"system";let i=n==="system"?u():n;const r=e.getAttribute("data-color-mode-forced");r&&(i=r),l(i),t["__NUXT_COLOR_MODE__"]={preference:n,value:i,getColorScheme:u,addColorScheme:l,removeColorScheme:d};function l(o){const s=""+o+"",a="";e.classList?e.classList.add(s):e.className+=" "+s,a&&e.setAttribute("data-"+a,o)}function d(o){const s=""+o+"",a="";e.classList?e.classList.remove(s):e.className=e.className.replace(new RegExp(s,"g"),""),a&&e.removeAttribute("data-"+a)}function f(o){return t.matchMedia("(prefers-color-scheme"+o+")")}function u(){if(t.matchMedia&&f("").media!=="not all"){for(const o of c)if(f(":"+o).matches)return o}return"light"}})();function getStorageValue(t,e){switch(t){case"localStorage":return window.localStorage.getItem(e);case"sessionStorage":return window.sessionStorage.getItem(e);case"cookie":return getCookie(e);default:return null}}function getCookie(t){const c=("; "+window.document.cookie).split("; "+t+"=");if(c.length===2)return c.pop()?.split(";").shift()}</script></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-ai-ready",buildId:"feeb28d1-669d-4cde-b5e6-08a29aab779a",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1776955857788,false]</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__nuxt-ai-ready/_nuxt/entry.BLpi4vfB.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js"><script type="module" src="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js" crossorigin></script><script>"use strict";(()=>{const t=window,e=document.documentElement,c=["dark","light"],n=getStorageValue("localStorage","nuxt-color-mode")||"system";let i=n==="system"?u():n;const r=e.getAttribute("data-color-mode-forced");r&&(i=r),l(i),t["__NUXT_COLOR_MODE__"]={preference:n,value:i,getColorScheme:u,addColorScheme:l,removeColorScheme:d};function l(o){const s=""+o+"",a="";e.classList?e.classList.add(s):e.className+=" "+s,a&&e.setAttribute("data-"+a,o)}function d(o){const s=""+o+"",a="";e.classList?e.classList.remove(s):e.className=e.className.replace(new RegExp(s,"g"),""),a&&e.removeAttribute("data-"+a)}function f(o){return t.matchMedia("(prefers-color-scheme"+o+")")}function u(){if(t.matchMedia&&f("").media!=="not all"){for(const o of c)if(f(":"+o).matches)return o}return"light"}})();function getStorageValue(t,e){switch(t){case"localStorage":return window.localStorage.getItem(e);case"sessionStorage":return window.sessionStorage.getItem(e);case"cookie":return getCookie(e);default:return null}}function getCookie(t){const c=("; "+window.document.cookie).split("; "+t+"=");if(c.length===2)return c.pop()?.split(";").shift()}</script></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-ai-ready",buildId:"cfed7300-fd87-4d26-98a1-6e1d8a608160",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1777105835626,false]</script></body></html>
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__nuxt-ai-ready/_nuxt/entry.BLpi4vfB.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js"><script type="module" src="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js" crossorigin></script><script>"use strict";(()=>{const t=window,e=document.documentElement,c=["dark","light"],n=getStorageValue("localStorage","nuxt-color-mode")||"system";let i=n==="system"?u():n;const r=e.getAttribute("data-color-mode-forced");r&&(i=r),l(i),t["__NUXT_COLOR_MODE__"]={preference:n,value:i,getColorScheme:u,addColorScheme:l,removeColorScheme:d};function l(o){const s=""+o+"",a="";e.classList?e.classList.add(s):e.className+=" "+s,a&&e.setAttribute("data-"+a,o)}function d(o){const s=""+o+"",a="";e.classList?e.classList.remove(s):e.className=e.className.replace(new RegExp(s,"g"),""),a&&e.removeAttribute("data-"+a)}function f(o){return t.matchMedia("(prefers-color-scheme"+o+")")}function u(){if(t.matchMedia&&f("").media!=="not all"){for(const o of c)if(f(":"+o).matches)return o}return"light"}})();function getStorageValue(t,e){switch(t){case"localStorage":return window.localStorage.getItem(e);case"sessionStorage":return window.sessionStorage.getItem(e);case"cookie":return getCookie(e);default:return null}}function getCookie(t){const c=("; "+window.document.cookie).split("; "+t+"=");if(c.length===2)return c.pop()?.split(";").shift()}</script></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-ai-ready",buildId:"feeb28d1-669d-4cde-b5e6-08a29aab779a",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1776955857789,false]</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__nuxt-ai-ready/_nuxt/entry.BLpi4vfB.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js"><script type="module" src="/__nuxt-ai-ready/_nuxt/B3Ds4CMX.js" crossorigin></script><script>"use strict";(()=>{const t=window,e=document.documentElement,c=["dark","light"],n=getStorageValue("localStorage","nuxt-color-mode")||"system";let i=n==="system"?u():n;const r=e.getAttribute("data-color-mode-forced");r&&(i=r),l(i),t["__NUXT_COLOR_MODE__"]={preference:n,value:i,getColorScheme:u,addColorScheme:l,removeColorScheme:d};function l(o){const s=""+o+"",a="";e.classList?e.classList.add(s):e.className+=" "+s,a&&e.setAttribute("data-"+a,o)}function d(o){const s=""+o+"",a="";e.classList?e.classList.remove(s):e.className=e.className.replace(new RegExp(s,"g"),""),a&&e.removeAttribute("data-"+a)}function f(o){return t.matchMedia("(prefers-color-scheme"+o+")")}function u(){if(t.matchMedia&&f("").media!=="not all"){for(const o of c)if(f(":"+o).matches)return o}return"light"}})();function getStorageValue(t,e){switch(t){case"localStorage":return window.localStorage.getItem(e);case"sessionStorage":return window.sessionStorage.getItem(e);case"cookie":return getCookie(e);default:return null}}function getCookie(t){const c=("; "+window.document.cookie).split("; "+t+"=");if(c.length===2)return c.pop()?.split(";").shift()}</script></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-ai-ready",buildId:"cfed7300-fd87-4d26-98a1-6e1d8a608160",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1777105835626,false]</script></body></html>
package/dist/module.d.mts CHANGED
@@ -55,6 +55,17 @@ interface ModulePublicRuntimeConfig {
55
55
  runtimeSyncSecret?: string;
56
56
  indexNow?: string;
57
57
  sitemapPrerendered: boolean;
58
+ i18n?: {
59
+ defaultLocale: string;
60
+ strategy: 'no_prefix' | 'prefix_except_default' | 'prefix' | 'prefix_and_default';
61
+ locales: Array<{
62
+ code: string;
63
+ hreflang: string;
64
+ name?: string;
65
+ nativeName?: string;
66
+ }>;
67
+ } | null;
68
+ ftsTokenizer?: string;
58
69
  }
59
70
  declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
60
71
 
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "nuxt": ">=4.0.0"
5
5
  },
6
6
  "configKey": "aiReady",
7
- "version": "1.2.0",
7
+ "version": "1.3.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -5,6 +5,7 @@ import { useLogger, useNuxt, addTypeTemplate, addTemplate, defineNuxtModule, cre
5
5
  import defu from 'defu';
6
6
  import { installNuxtSiteConfig, useSiteConfig, withSiteUrl } from 'nuxt-site-config/kit';
7
7
  import { setupDevToolsUI } from 'nuxtseo-shared/devtools';
8
+ import { resolveNuxtContentVersion } from 'nuxtseo-shared/kit';
8
9
  import { readPackageJSON } from 'pkg-types';
9
10
  import { parseSitemapXml } from '@nuxtjs/sitemap/utils';
10
11
  import { colorize } from 'consola/utils';
@@ -13,6 +14,7 @@ import { initSchema, computeContentHash, insertPage, queryAllPages, exportDbDump
13
14
  import { comparePageHashes, submitToIndexNowShared } from '../dist/runtime/server/utils/indexnow-shared.js';
14
15
  import { buildLlmsFullTxtHeader, formatPageForLlmsFullTxt } from '../dist/runtime/server/utils/llms-full.js';
15
16
  import { isAbsolute, join as join$1 } from 'pathe';
17
+ import { resolveI18nConfig } from 'nuxtseo-shared/i18n';
16
18
 
17
19
  const logger = useLogger("nuxt-ai-ready");
18
20
 
@@ -59,7 +61,7 @@ async function submitIndexNow(changedRoutes, addedRoutes, siteUrl, indexNow) {
59
61
  logger.warn(`[indexnow] Failed to submit: ${result.error}`);
60
62
  }
61
63
  }
62
- function createCrawlerState(dbPath, llmsFullTxtPath, siteInfo, llmsTxtConfig, indexNow, concurrency = 10) {
64
+ function createCrawlerState(dbPath, llmsFullTxtPath, siteInfo, llmsTxtConfig, indexNow, concurrency = 10, ftsTokenizer, i18n) {
63
65
  return {
64
66
  prerenderedRoutes: /* @__PURE__ */ new Set(),
65
67
  errorRoutes: /* @__PURE__ */ new Set(),
@@ -70,7 +72,9 @@ function createCrawlerState(dbPath, llmsFullTxtPath, siteInfo, llmsTxtConfig, in
70
72
  siteInfo,
71
73
  llmsTxtConfig,
72
74
  indexNow,
73
- concurrency
75
+ concurrency,
76
+ ftsTokenizer,
77
+ i18n
74
78
  };
75
79
  }
76
80
  async function initCrawler(state) {
@@ -92,8 +96,8 @@ async function initCrawler(state) {
92
96
  }
93
97
  };
94
98
  state.db = db;
95
- await initSchema(db);
96
- logger.debug(`Crawler initialized with SQLite at ${state.dbPath}`);
99
+ await initSchema(db, { ftsTokenizer: state.ftsTokenizer });
100
+ logger.debug(`Crawler initialized with SQLite at ${state.dbPath} (tokenizer: ${state.ftsTokenizer || "default"})`);
97
101
  }
98
102
  if (state.llmsFullTxtPath) {
99
103
  logger.debug(`Creating directory for llms-full.txt: ${dirname(state.llmsFullTxtPath)}`);
@@ -108,6 +112,16 @@ async function initCrawler(state) {
108
112
  function flattenHeadings(headings) {
109
113
  return (headings || []).map((h) => Object.entries(h).map(([tag, text]) => `${tag}:${text}`).join("")).join("|");
110
114
  }
115
+ function resolveRouteLocale(route, i18n) {
116
+ if (!i18n)
117
+ return "";
118
+ if (i18n.strategy === "no_prefix")
119
+ return i18n.defaultLocale;
120
+ const segments = route.split("/").filter(Boolean);
121
+ const first = segments[0];
122
+ const matched = first ? i18n.locales.find((l) => l.code === first) : void 0;
123
+ return matched ? matched.code : i18n.defaultLocale;
124
+ }
111
125
  async function processMarkdownRoute(state, nuxt, route, parsed, lastmod, options) {
112
126
  const { markdown, title, description, headings, keywords, updatedAt: metaUpdatedAt } = parsed;
113
127
  let updatedAt = (lastmod instanceof Date ? lastmod.toISOString() : lastmod) || (/* @__PURE__ */ new Date()).toISOString();
@@ -127,7 +141,8 @@ async function processMarkdownRoute(state, nuxt, route, parsed, lastmod, options
127
141
  headings: flattenHeadings(headings),
128
142
  keywords: keywords || [],
129
143
  contentHash,
130
- updatedAt
144
+ updatedAt,
145
+ locale: resolveRouteLocale(route, state.i18n)
131
146
  });
132
147
  }
133
148
  if (state.llmsFullTxtPath && !options?.skipLlmsFullTxt) {
@@ -247,11 +262,20 @@ async function prerenderRoute(nitro, route) {
247
262
  nitro._prerenderedRoutes.push(_route);
248
263
  return stat(filePath);
249
264
  }
250
- function setupPrerenderHandler(options, dbPath, siteInfo, llmsTxtConfig, indexNow) {
265
+ function setupPrerenderHandler(options, dbPath, siteInfo, llmsTxtConfig, indexNow, extras = {}) {
251
266
  const nuxt = useNuxt();
252
267
  nuxt.hooks.hook("nitro:init", async (nitro) => {
253
268
  const llmsFullTxtPath = join(nitro.options.output.publicDir, "llms-full.txt");
254
- const state = createCrawlerState(dbPath, llmsFullTxtPath, siteInfo, llmsTxtConfig, indexNow, options.prerender?.concurrency);
269
+ const state = createCrawlerState(
270
+ dbPath,
271
+ llmsFullTxtPath,
272
+ siteInfo,
273
+ llmsTxtConfig,
274
+ indexNow,
275
+ options.prerender?.concurrency,
276
+ extras.ftsTokenizer,
277
+ extras.i18n
278
+ );
255
279
  let initPromise = null;
256
280
  nitro.hooks.hook("prerender:generate", async (route) => {
257
281
  if (route.error) {
@@ -285,7 +309,8 @@ function setupPrerenderHandler(options, dbPath, siteInfo, llmsTxtConfig, indexNo
285
309
  headings: "",
286
310
  keywords: [],
287
311
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
288
- isError: true
312
+ isError: true,
313
+ locale: resolveRouteLocale(route, state.i18n)
289
314
  });
290
315
  }
291
316
  logger.debug(`Wrote ${state.errorRoutes.size} error routes to database`);
@@ -454,6 +479,16 @@ declare module '#ai-ready-virtual/db-provider.mjs' {
454
479
  declare module '#ai-ready-virtual/db-schema.mjs' {
455
480
  export * from '#ai-ready/server/db/schema/sqlite'
456
481
  }
482
+
483
+ declare module '#ai-ready-virtual/content-lookup.mjs' {
484
+ import type { H3Event } from 'h3'
485
+ export function lookupContentPage(event: H3Event, path: string): Promise<{
486
+ markdown: string
487
+ title?: string
488
+ description?: string
489
+ updatedAt?: string
490
+ } | null>
491
+ }
457
492
  `
458
493
  });
459
494
  }
@@ -487,6 +522,39 @@ function refineDatabaseConfig(config, rootDir) {
487
522
  };
488
523
  }
489
524
 
525
+ const CJK_PREFIXES = ["zh", "ja", "ko"];
526
+ function hasCjkLocale(i18n) {
527
+ return i18n.locales.some((l) => CJK_PREFIXES.some((p) => l.code.startsWith(p) || l.hreflang.startsWith(p)));
528
+ }
529
+ function toRuntimeI18nConfig(auto) {
530
+ return {
531
+ defaultLocale: auto.defaultLocale,
532
+ strategy: auto.strategy,
533
+ locales: auto.locales.map((l) => {
534
+ const raw = l;
535
+ return {
536
+ code: l.code,
537
+ hreflang: l._hreflang || raw.language || l.code,
538
+ name: raw.name,
539
+ nativeName: raw.nativeName ?? raw.name
540
+ };
541
+ })
542
+ };
543
+ }
544
+ async function detectI18n(opts = {}) {
545
+ if (opts.autoI18n === false)
546
+ return null;
547
+ const auto = await resolveI18nConfig({ warn: (msg) => logger.warn(msg) });
548
+ if (!auto)
549
+ return null;
550
+ if (!auto.locales?.length) {
551
+ logger.warn("[ai-ready] @nuxtjs/i18n detected but no locales configured. i18n integration disabled.");
552
+ return null;
553
+ }
554
+ logger.debug(`[ai-ready] i18n detected: ${auto.locales.length} locales, strategy=${auto.strategy}, default=${auto.defaultLocale}`);
555
+ return toRuntimeI18nConfig(auto);
556
+ }
557
+
490
558
  const module$1 = defineNuxtModule({
491
559
  meta: {
492
560
  name: "nuxt-ai-ready",
@@ -547,6 +615,11 @@ const module$1 = defineNuxtModule({
547
615
  logger.warn("`mdreamOptions.preset` is deprecated. Use `mdreamOptions: { minimal: true }` instead. See https://github.com/harlan-zw/nuxt-ai-ready/releases/tag/v1.0.0");
548
616
  }
549
617
  await installNuxtSiteConfig();
618
+ const i18nConfig = await detectI18n({ autoI18n: config.autoI18n });
619
+ if (i18nConfig) {
620
+ logger.info(`i18n detected: ${i18nConfig.locales.length} locales (default: ${i18nConfig.defaultLocale}, strategy: ${i18nConfig.strategy})`);
621
+ }
622
+ const ftsTokenizer = i18nConfig && hasCjkLocale(i18nConfig) ? "trigram" : "unicode61 remove_diacritics 2";
550
623
  nuxt.options.nitro.alias = nuxt.options.nitro.alias || {};
551
624
  nuxt.options.alias["#ai-ready"] = resolve("./runtime");
552
625
  const preset = String(nuxt.options.nitro.preset || "");
@@ -581,6 +654,8 @@ const module$1 = defineNuxtModule({
581
654
  nuxt.options.nitro.scanDirs.push(
582
655
  resolve("./runtime/server/utils")
583
656
  );
657
+ const contentVersion = await resolveNuxtContentVersion();
658
+ const hasNuxtContentV3 = !!(contentVersion && contentVersion.version === 3);
584
659
  if (typeof config.contentSignal === "object") {
585
660
  const robotsOpts = nuxt.options.robots !== false ? nuxt.options.robots : {};
586
661
  nuxt.options.robots = robotsOpts;
@@ -743,13 +818,13 @@ export async function readPageDataFromFilesystem() {
743
818
  if (nodeVersion >= 22) {
744
819
  const { DatabaseSync } = await import('node' + ':sqlite')
745
820
  const db = new DatabaseSync(dbPath, { open: true })
746
- rows = db.prepare('SELECT route, title, description, markdown, headings, keywords, updated_at, is_error FROM ai_ready_pages').all()
821
+ rows = db.prepare('SELECT route, title, description, markdown, headings, keywords, updated_at, is_error, locale FROM ai_ready_pages').all()
747
822
  db.close()
748
823
  }
749
824
  else {
750
825
  const Database = (await import('better-sqlite3')).default
751
826
  const db = new Database(dbPath, { readonly: true })
752
- rows = db.prepare('SELECT route, title, description, markdown, headings, keywords, updated_at, is_error FROM ai_ready_pages').all()
827
+ rows = db.prepare('SELECT route, title, description, markdown, headings, keywords, updated_at, is_error, locale FROM ai_ready_pages').all()
753
828
  db.close()
754
829
  }
755
830
 
@@ -761,6 +836,7 @@ export async function readPageDataFromFilesystem() {
761
836
  headings: r.headings,
762
837
  keywords: JSON.parse(r.keywords || '[]'),
763
838
  updatedAt: r.updated_at,
839
+ locale: r.locale || '',
764
840
  }))
765
841
  const errorRoutes = rows.filter(r => r.is_error).map(r => r.route)
766
842
 
@@ -792,6 +868,34 @@ export const logger = createConsola({
792
868
  mcp: { enabled: hasMCP, tools: hasMCP && config.mcp?.tools !== false, resources: hasMCP && config.mcp?.resources !== false },
793
869
  cron: !!config.cron
794
870
  })}`;
871
+ nitroConfig.virtual["#ai-ready-virtual/content-lookup.mjs"] = hasNuxtContentV3 ? `
872
+ import { queryCollection } from '@nuxt/content/server'
873
+ import manifest from '#content/manifest'
874
+ import { stringify } from 'minimark/stringify'
875
+
876
+ const pageCollections = Object.entries(manifest)
877
+ .filter(([, info]) => info.type === 'page')
878
+ .map(([name]) => name)
879
+
880
+ export async function lookupContentPage(event, path) {
881
+ if (!pageCollections.length) return null
882
+ const candidates = path === '/' ? ['/'] : [path, path.replace(/\\/$/, '')]
883
+ for (const collection of pageCollections) {
884
+ for (const candidate of candidates) {
885
+ const page = await queryCollection(event, collection).path(candidate).first().catch(() => null)
886
+ if (!page) continue
887
+ const markdown = stringify({ ...page.body, type: 'minimark' }, { format: 'markdown/html' })
888
+ return {
889
+ markdown,
890
+ title: page.title,
891
+ description: page.description,
892
+ updatedAt: page.seo?.articleModifiedTime || page.updatedAt,
893
+ }
894
+ }
895
+ }
896
+ return null
897
+ }
898
+ ` : `export async function lookupContentPage() { return null }`;
795
899
  });
796
900
  const database = refineDatabaseConfig(config.database || {}, nuxt.options.rootDir);
797
901
  nuxt.options.runtimeConfig["nuxt-ai-ready"] = {
@@ -815,7 +919,9 @@ export const logger = createConsola({
815
919
  },
816
920
  runtimeSyncSecret,
817
921
  indexNow,
818
- sitemapPrerendered
922
+ sitemapPrerendered,
923
+ i18n: i18nConfig,
924
+ ftsTokenizer
819
925
  };
820
926
  addServerHandler({
821
927
  middleware: true,
@@ -875,7 +981,7 @@ export const logger = createConsola({
875
981
  name: siteConfig.name,
876
982
  url: siteConfig.url,
877
983
  description: siteConfig.description
878
- }, mergedLlmsTxt, indexNow);
984
+ }, mergedLlmsTxt, indexNow, { ftsTokenizer, i18n: i18nConfig });
879
985
  }
880
986
  addServerPlugin(resolve("./runtime/server/plugins/db-lifecycle"));
881
987
  setupDevToolsUI({
@@ -2,8 +2,21 @@ import { useRuntimeConfig } from "nitropack/runtime";
2
2
  import { getSiteConfig } from "#site-config/server/composables/getSiteConfig";
3
3
  import { normalizeLlmsTxtConfig } from "./llms-txt-format.js";
4
4
  import { queryPages } from "./server/db/queries.js";
5
+ import { localePath, resolveLocaleFromRoute } from "./server/utils/i18n.js";
5
6
  import { fetchSitemapUrls } from "./server/utils/sitemap.js";
6
7
  export { normalizeLlmsTxtConfig };
8
+ function formatAvailableLanguagesSection(i18n, pageCounts) {
9
+ const lines = ["## Available Languages on Website", ""];
10
+ for (const locale of i18n.locales) {
11
+ const isDefault = locale.code === i18n.defaultLocale;
12
+ const prefix = localePath("/", locale.code, i18n);
13
+ const count = pageCounts.get(locale.code) ?? 0;
14
+ const display = locale.nativeName ? `${locale.nativeName} (${locale.code})` : locale.name ? `${locale.name} (${locale.code})` : locale.code;
15
+ const suffix = isDefault ? "Content included below" : "Visit website for content";
16
+ lines.push(`- ${display} - ${count} pages - ${prefix} - ${suffix}`);
17
+ }
18
+ return lines;
19
+ }
7
20
  function getGroupPrefix(url, depth) {
8
21
  const segments = url.split("/").filter(Boolean);
9
22
  if (segments.length === 0)
@@ -101,6 +114,7 @@ export async function buildLlmsTxt(event) {
101
114
  const sitemapConfig = runtimeConfig.sitemap;
102
115
  const siteConfig = getSiteConfig(event);
103
116
  const llmsTxtConfig = aiReadyConfig.llmsTxt;
117
+ const i18n = aiReadyConfig.i18n;
104
118
  const parts = [];
105
119
  parts.push(`# ${siteConfig.name || siteConfig.url}`);
106
120
  if (siteConfig.description) {
@@ -137,19 +151,38 @@ Canonical Origin: ${siteConfig.url}`);
137
151
  const prerendered = [];
138
152
  for (const page of pages) {
139
153
  seenPaths.add(page.route);
140
- prerendered.push({ pathname: page.route, title: page.title, description: page.description });
154
+ prerendered.push({ pathname: page.route, title: page.title, description: page.description, locale: page.locale });
141
155
  }
142
156
  const devModeHint = import.meta.dev && prerendered.length === 0 ? " (dev mode - run `nuxi generate` for page titles)" : "";
143
157
  const other = [];
144
158
  for (const url of urls) {
145
159
  const pathname = url.loc.startsWith("/") ? url.loc : new URL(url.loc).pathname;
146
160
  if (!seenPaths.has(pathname) && !errorSet.has(pathname)) {
147
- other.push({ pathname });
161
+ const locale = i18n ? resolveLocaleFromRoute(pathname, i18n).locale : void 0;
162
+ other.push({ pathname, locale });
148
163
  seenPaths.add(pathname);
149
164
  }
150
165
  }
151
- const sortedPrerendered = sortPagesByPath(prerendered);
152
- const sortedOther = sortPagesByPath(other);
166
+ if (i18n) {
167
+ const pageCounts = /* @__PURE__ */ new Map();
168
+ for (const locale of i18n.locales) pageCounts.set(locale.code, 0);
169
+ for (const p of [...prerendered, ...other]) {
170
+ const code = p.locale || resolveLocaleFromRoute(p.pathname, i18n).locale;
171
+ pageCounts.set(code, (pageCounts.get(code) ?? 0) + 1);
172
+ }
173
+ parts.push(...formatAvailableLanguagesSection(i18n, pageCounts));
174
+ parts.push("");
175
+ }
176
+ const isDefaultLocale = (item) => {
177
+ if (!i18n)
178
+ return true;
179
+ const code = item.locale || resolveLocaleFromRoute(item.pathname, i18n).locale;
180
+ return code === i18n.defaultLocale;
181
+ };
182
+ const filteredPrerendered = i18n ? prerendered.filter(isDefaultLocale) : prerendered;
183
+ const filteredOther = i18n ? other.filter(isDefaultLocale) : other;
184
+ const sortedPrerendered = sortPagesByPath(filteredPrerendered);
185
+ const sortedOther = sortPagesByPath(filteredOther);
153
186
  if (sortedPrerendered.length > 0 && sortedOther.length > 0) {
154
187
  parts.push(`## Prerendered Pages${devModeHint}
155
188
  `);
@@ -99,7 +99,9 @@ export declare function setInfoValue(event: H3Event | undefined, key: string, va
99
99
  */
100
100
  export declare function deleteInfoValue(event: H3Event | undefined, key: string): Promise<void>;
101
101
  /**
102
- * Initialize database schema
102
+ * Initialize database schema. Rebuilds on SCHEMA_VERSION change or when the
103
+ * SQLite FTS5 tokenizer differs from the one baked into the existing virtual
104
+ * table (Postgres has no FTS5; tokenizer comparison is SQLite-only).
103
105
  */
104
106
  export declare function initSchema(event?: H3Event): Promise<void>;
105
107
  /**