nuxt-ai-ready 0.4.4 → 0.5.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.
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "nuxt": ">=4.0.0"
5
5
  },
6
6
  "configKey": "aiReady",
7
- "version": "0.4.4",
7
+ "version": "0.5.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { appendFile, mkdir, writeFile, stat, access } from 'node:fs/promises';
1
+ import { appendFile, mkdir, writeFile, readFile, stat, access } from 'node:fs/promises';
2
2
  import { join, dirname } from 'node:path';
3
3
  import { useLogger, useNuxt, defineNuxtModule, createResolver, addTypeTemplate, hasNuxtModule, addServerHandler, addPlugin } from '@nuxt/kit';
4
4
  import defu from 'defu';
@@ -231,7 +231,7 @@ async function crawlSitemapEntries(state, nuxt, nitro, entries) {
231
231
  try {
232
232
  parsed = JSON.parse(res);
233
233
  } catch (err) {
234
- logger.debug(`Skipping ${route}: Response is not JSON (likely HTML instead of markdown conversion)`);
234
+ logger.debug(`Skipping ${route}: Response is not JSON (likely HTML instead of markdown conversion)`, err);
235
235
  continue;
236
236
  }
237
237
  await processMarkdownRoute(state, nuxt, route, parsed, lastmod);
@@ -361,6 +361,26 @@ function setupPrerenderHandler(pageDataPath, siteInfo, llmsTxtConfig) {
361
361
  }
362
362
  logger.debug(`Wrote ${state.errorRoutes.size} error routes to page data`);
363
363
  }
364
+ if (state.pageDataPath) {
365
+ const jsonlContent = await readFile(state.pageDataPath, "utf-8").catch(() => "");
366
+ if (jsonlContent) {
367
+ const entries = jsonlContent.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
368
+ const pages = entries.filter((e) => !e._error).map((p) => ({
369
+ route: p.route,
370
+ title: p.title,
371
+ description: p.description,
372
+ headings: p.headings,
373
+ updatedAt: p.updatedAt
374
+ }));
375
+ const errorRoutes = entries.filter((e) => e._error).map((e) => e.route);
376
+ const jsonContent = JSON.stringify({ pages, errorRoutes });
377
+ const serverAssetsDir = join(nitro.options.output.serverDir, "chunks/raw/ai-ready-data");
378
+ await mkdir(serverAssetsDir, { recursive: true });
379
+ const serverAssetsJsonPath = join(serverAssetsDir, "pages.json");
380
+ await writeFile(serverAssetsJsonPath, jsonContent, "utf-8");
381
+ logger.debug(`Wrote ${pages.length} pages to server assets`);
382
+ }
383
+ }
364
384
  const llmsStats = await prerenderRoute(nitro, "/llms.txt");
365
385
  const llmsFullStats = await stat(state.llmsFullTxtPath);
366
386
  const kb = (b) => (b / 1024).toFixed(1);
@@ -480,14 +500,17 @@ declare module 'nitropack/types' {
480
500
  }
481
501
 
482
502
  declare module '#ai-ready-virtual/read-page-data.mjs' {
483
- export function readPageDataFromFilesystem(): Promise<Array<{
484
- route: string
485
- title: string
486
- description: string
487
- headings: string
488
- updatedAt: string
489
- markdown: string
490
- }> | null>
503
+ export function readPageDataFromFilesystem(): Promise<{
504
+ pages: Array<{
505
+ route: string
506
+ title: string
507
+ description: string
508
+ headings: string
509
+ updatedAt: string
510
+ markdown: string
511
+ }>
512
+ errorRoutes: string[]
513
+ }>
491
514
  }
492
515
 
493
516
  declare module '#ai-ready-virtual/page-data.mjs' {
@@ -568,6 +591,12 @@ export async function readPageDataFromFilesystem() {
568
591
  `;
569
592
  nitroConfig.virtual["#ai-ready-virtual/page-data.mjs"] = `export const pages = []
570
593
  export const errorRoutes = []`;
594
+ nitroConfig.serverAssets = nitroConfig.serverAssets || [];
595
+ nitroConfig.serverAssets.push({
596
+ baseName: "ai-ready-data",
597
+ dir: "./.nitro/ai-ready-data"
598
+ // Placeholder, actual data written during prerender
599
+ });
571
600
  });
572
601
  nuxt.options.runtimeConfig["nuxt-ai-ready"] = {
573
602
  version: version || "0.0.0",
@@ -597,6 +626,9 @@ export const errorRoutes = []`;
597
626
  }
598
627
  addServerHandler({ route: "/llms.txt", handler: resolve("./runtime/server/routes/llms.txt.get") });
599
628
  addServerHandler({ route: "/llms-full.txt", handler: resolve("./runtime/server/routes/llms-full.txt.get") });
629
+ if (config.debug) {
630
+ addServerHandler({ route: "/__ai-ready-debug", handler: resolve("./runtime/server/routes/__ai-ready-debug.get") });
631
+ }
600
632
  const isStatic = nuxt.options.nitro.static || nuxt.options._generate || false;
601
633
  const hasPrerenderedRoutes = nuxt.options.nitro.prerender?.routes?.length;
602
634
  const isSPA = nuxt.options.ssr === false;
@@ -0,0 +1,46 @@
1
+ interface DebugInfo {
2
+ version: string;
3
+ environment: {
4
+ isDev: boolean;
5
+ isPrerender: boolean;
6
+ mode: 'development' | 'prerender' | 'production';
7
+ };
8
+ config: {
9
+ debug: boolean;
10
+ cacheMaxAgeSeconds: number;
11
+ mdreamOptions: unknown;
12
+ };
13
+ pageData: {
14
+ source: string;
15
+ pageCount: number;
16
+ pages: Array<{
17
+ route: string;
18
+ title: string;
19
+ hasDescription: boolean;
20
+ hasHeadings: boolean;
21
+ }>;
22
+ errorRoutes: string[];
23
+ };
24
+ jsonFile: {
25
+ available: boolean;
26
+ pageCount: number;
27
+ source: string;
28
+ };
29
+ virtualModules: {
30
+ pageDataModule: {
31
+ available: boolean;
32
+ pagesCount: number;
33
+ errorRoutesCount: number;
34
+ };
35
+ readPageDataModule: {
36
+ available: boolean;
37
+ note: string;
38
+ };
39
+ };
40
+ diagnostics: {
41
+ issues: string[];
42
+ suggestions: string[];
43
+ };
44
+ }
45
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<DebugInfo>>;
46
+ export default _default;
@@ -0,0 +1,113 @@
1
+ import { createError, eventHandler, setHeader } from "h3";
2
+ import { useRuntimeConfig, useStorage } from "nitropack/runtime";
3
+ import { getErrorRoutes, getPages, getPagesList } from "../utils/pageData.js";
4
+ export default eventHandler(async (event) => {
5
+ const runtimeConfig = useRuntimeConfig(event)["nuxt-ai-ready"];
6
+ if (!runtimeConfig.debug) {
7
+ throw createError({
8
+ statusCode: 404,
9
+ message: "Not Found"
10
+ });
11
+ }
12
+ const isDev = import.meta.dev;
13
+ const isPrerender = import.meta.prerender ?? false;
14
+ let mode;
15
+ if (isDev) {
16
+ mode = "development";
17
+ } else if (isPrerender) {
18
+ mode = "prerender";
19
+ } else {
20
+ mode = "production";
21
+ }
22
+ const pages = await getPages();
23
+ const pagesList = await getPagesList();
24
+ const errorRoutes = await getErrorRoutes();
25
+ let source;
26
+ if (isDev) {
27
+ source = "empty (dev mode returns empty Map)";
28
+ } else if (isPrerender) {
29
+ source = "#ai-ready-virtual/read-page-data.mjs (reads from filesystem)";
30
+ } else {
31
+ source = "useStorage('assets:ai-ready-data') (server assets)";
32
+ }
33
+ let pageDataModuleInfo = { available: false, pagesCount: 0, errorRoutesCount: 0 };
34
+ let readPageDataModuleInfo = { available: false, note: "" };
35
+ try {
36
+ const m = await import("#ai-ready-virtual/page-data.mjs");
37
+ pageDataModuleInfo = {
38
+ available: true,
39
+ pagesCount: Array.isArray(m.pages) ? m.pages.length : 0,
40
+ errorRoutesCount: Array.isArray(m.errorRoutes) ? m.errorRoutes.length : 0
41
+ };
42
+ } catch {
43
+ pageDataModuleInfo.available = false;
44
+ }
45
+ try {
46
+ const m = await import("#ai-ready-virtual/read-page-data.mjs");
47
+ readPageDataModuleInfo = {
48
+ available: typeof m.readPageDataFromFilesystem === "function",
49
+ note: isPrerender ? "Active - reading from filesystem" : "Available but only works during prerender"
50
+ };
51
+ } catch {
52
+ readPageDataModuleInfo = { available: false, note: "Module not available" };
53
+ }
54
+ const storage = useStorage("assets:ai-ready-data");
55
+ const storageData = await storage.getItem("pages.json");
56
+ const jsonFileStatus = {
57
+ available: !!storageData,
58
+ pageCount: storageData?.pages?.length ?? 0,
59
+ source: "useStorage('assets:ai-ready-data')"
60
+ };
61
+ const issues = [];
62
+ const suggestions = [];
63
+ if (mode === "development") {
64
+ issues.push("Development mode: page data is intentionally empty");
65
+ suggestions.push("Run `nuxi generate` or `nuxi build --prerender` to generate page data");
66
+ } else if (mode === "production" && pages.size === 0) {
67
+ if (!jsonFileStatus.available) {
68
+ issues.push("Production mode with no page data - server assets not found");
69
+ suggestions.push("Run `nuxi generate` or `nuxi build --prerender` to generate the page data");
70
+ } else {
71
+ issues.push("Server assets exist but returned empty page data");
72
+ suggestions.push("Check if pages were prerendered correctly");
73
+ }
74
+ } else if (mode === "prerender" && pages.size === 0) {
75
+ issues.push("Prerender mode but no pages found");
76
+ suggestions.push("Check if page-data.jsonl exists in .nuxt/.data/ai-ready/");
77
+ }
78
+ const debugInfo = {
79
+ version: runtimeConfig.version || "unknown",
80
+ environment: {
81
+ isDev,
82
+ isPrerender,
83
+ mode
84
+ },
85
+ config: {
86
+ debug: runtimeConfig.debug,
87
+ cacheMaxAgeSeconds: runtimeConfig.cacheMaxAgeSeconds,
88
+ mdreamOptions: runtimeConfig.mdreamOptions
89
+ },
90
+ pageData: {
91
+ source,
92
+ pageCount: pages.size,
93
+ pages: pagesList.map((p) => ({
94
+ route: p.route,
95
+ title: p.title,
96
+ hasDescription: !!p.description,
97
+ hasHeadings: !!p.headings
98
+ })),
99
+ errorRoutes: Array.from(errorRoutes)
100
+ },
101
+ jsonFile: jsonFileStatus,
102
+ virtualModules: {
103
+ pageDataModule: pageDataModuleInfo,
104
+ readPageDataModule: readPageDataModuleInfo
105
+ },
106
+ diagnostics: {
107
+ issues,
108
+ suggestions
109
+ }
110
+ };
111
+ setHeader(event, "Content-Type", "application/json; charset=utf-8");
112
+ return debugInfo;
113
+ });
@@ -10,8 +10,6 @@ export interface PageEntry {
10
10
  export interface PageData extends PageEntry {
11
11
  markdown: string;
12
12
  }
13
- /** Read page data - returns page data indexed by route */
14
- export declare function getPages(): Promise<Map<string, PageEntry>>;
15
13
  /** Page list item for MCP tools/resources */
16
14
  export interface PageListItem {
17
15
  route: string;
@@ -19,7 +17,9 @@ export interface PageListItem {
19
17
  description: string;
20
18
  headings?: string;
21
19
  }
22
- /** Get pages as flat list for MCP consumption */
23
- export declare function getPagesList(): Promise<PageListItem[]>;
20
+ /** Read page data - returns page data indexed by route */
21
+ export declare function getPages(): Promise<Map<string, PageEntry>>;
24
22
  /** Get error routes detected during prerender */
25
23
  export declare function getErrorRoutes(): Promise<Set<string>>;
24
+ /** Get pages as flat list for MCP consumption */
25
+ export declare function getPagesList(): Promise<PageListItem[]>;
@@ -1,22 +1,17 @@
1
+ import { useStorage } from "nitropack/runtime";
1
2
  export async function getPages() {
2
3
  if (import.meta.dev)
3
4
  return /* @__PURE__ */ new Map();
4
- if (import.meta.prerender) {
5
- const data = await readPrerenderedData();
6
- return data.pages;
7
- }
8
- const m = await import("#ai-ready-virtual/page-data.mjs");
9
- return m.pages?.length ? new Map(m.pages.map((p) => [p.route, p])) : /* @__PURE__ */ new Map();
5
+ if (import.meta.prerender)
6
+ return (await readPrerenderedData()).pages;
7
+ return (await readServerAssets()).pages;
10
8
  }
11
- async function readPrerenderedData() {
12
- if (!import.meta.prerender)
13
- return { pages: /* @__PURE__ */ new Map(), errorRoutes: /* @__PURE__ */ new Set() };
14
- const m = await import("#ai-ready-virtual/read-page-data.mjs");
15
- const data = await m.readPageDataFromFilesystem();
16
- return {
17
- pages: data.pages?.length ? new Map(data.pages.map((p) => [p.route, p])) : /* @__PURE__ */ new Map(),
18
- errorRoutes: new Set(data.errorRoutes || [])
19
- };
9
+ export async function getErrorRoutes() {
10
+ if (import.meta.dev)
11
+ return /* @__PURE__ */ new Set();
12
+ if (import.meta.prerender)
13
+ return (await readPrerenderedData()).errorRoutes;
14
+ return (await readServerAssets()).errorRoutes;
20
15
  }
21
16
  export async function getPagesList() {
22
17
  const pages = await getPages();
@@ -27,13 +22,21 @@ export async function getPagesList() {
27
22
  headings: p.headings || void 0
28
23
  }));
29
24
  }
30
- export async function getErrorRoutes() {
31
- if (import.meta.dev)
32
- return /* @__PURE__ */ new Set();
33
- if (import.meta.prerender) {
34
- const data = await readPrerenderedData();
35
- return data.errorRoutes;
36
- }
37
- const m = await import("#ai-ready-virtual/page-data.mjs");
38
- return new Set(m.errorRoutes || []);
25
+ async function readServerAssets() {
26
+ const storage = useStorage("assets:ai-ready-data");
27
+ const data = await storage.getItem("pages.json");
28
+ if (!data)
29
+ return { pages: /* @__PURE__ */ new Map(), errorRoutes: /* @__PURE__ */ new Set() };
30
+ return {
31
+ pages: new Map(data.pages?.map((p) => [p.route, p]) || []),
32
+ errorRoutes: new Set(data.errorRoutes || [])
33
+ };
34
+ }
35
+ async function readPrerenderedData() {
36
+ const m = await import("#ai-ready-virtual/read-page-data.mjs");
37
+ const data = await m.readPageDataFromFilesystem();
38
+ return {
39
+ pages: new Map(data.pages?.map((p) => [p.route, p]) || []),
40
+ errorRoutes: new Set(data.errorRoutes || [])
41
+ };
39
42
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-ai-ready",
3
3
  "type": "module",
4
- "version": "0.4.4",
4
+ "version": "0.5.0",
5
5
  "description": "Best practice AI & LLM discoverability for Nuxt sites.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -50,17 +50,17 @@
50
50
  "unstorage": "^1.17.3"
51
51
  },
52
52
  "devDependencies": {
53
- "@antfu/eslint-config": "^6.7.1",
53
+ "@antfu/eslint-config": "^6.7.3",
54
54
  "@arethetypeswrong/cli": "^0.18.2",
55
55
  "@headlessui/vue": "^1.7.23",
56
- "@nuxt/content": "^3.9.0",
56
+ "@nuxt/content": "^3.10.0",
57
57
  "@nuxt/devtools-ui-kit": "^3.1.1",
58
58
  "@nuxt/module-builder": "^1.0.2",
59
59
  "@nuxt/test-utils": "^3.21.0",
60
60
  "@nuxtjs/color-mode": "^4.0.0",
61
61
  "@nuxtjs/eslint-config-typescript": "^12.1.0",
62
62
  "@nuxtjs/i18n": "^10.2.1",
63
- "@nuxtjs/mcp-toolkit": "^0.5.2",
63
+ "@nuxtjs/mcp-toolkit": "^0.6.0",
64
64
  "@nuxtjs/robots": "^5.6.7",
65
65
  "@nuxtjs/sitemap": "7.5.0",
66
66
  "@vitest/coverage-v8": "^4.0.16",
@@ -79,7 +79,7 @@
79
79
  "vitest": "^4.0.16",
80
80
  "vue": "^3.5.26",
81
81
  "vue-router": "^4.6.4",
82
- "vue-tsc": "^3.1.8",
82
+ "vue-tsc": "^3.2.1",
83
83
  "zod": "^4.2.1"
84
84
  },
85
85
  "resolutions": {