nuxt-ai-ready 0.0.3 → 0.1.2
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.d.mts +6 -29
- package/dist/module.json +1 -1
- package/dist/module.mjs +51 -68
- package/dist/runtime/types.d.ts +18 -0
- package/package.json +5 -5
package/dist/module.d.mts
CHANGED
|
@@ -1,36 +1,8 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
-
import {
|
|
2
|
+
import { BulkChunk, ModuleOptions } from '../dist/runtime/types.js';
|
|
3
3
|
export { BulkChunk, ModuleOptions } from '../dist/runtime/types.js';
|
|
4
|
-
import { ProcessedFile } from 'mdream/llms-txt';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Hook payload for mdream:llms-txt
|
|
8
|
-
* Called after mdream has generated llms.txt, before writing to disk
|
|
9
|
-
*
|
|
10
|
-
* IMPORTANT: This uses a mutable pattern. Hooks should modify the content
|
|
11
|
-
* and fullContent properties directly rather than returning values.
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* nuxt.hooks.hook('mdream:llms-txt', async (payload) => {
|
|
15
|
-
* payload.content += '\n\n## Custom Section\n\nAdded by hook!'
|
|
16
|
-
* payload.fullContent += '\n\n## Custom Section (Full)\n\nAdded by hook!'
|
|
17
|
-
* })
|
|
18
|
-
*/
|
|
19
|
-
interface LlmsTxtGeneratePayload {
|
|
20
|
-
/** Current llms.txt content - modify this directly */
|
|
21
|
-
content: string;
|
|
22
|
-
/** Current llms-full.txt content - modify this directly */
|
|
23
|
-
fullContent: string;
|
|
24
|
-
/** All routes with their metadata (read-only) */
|
|
25
|
-
pages: ProcessedFile[];
|
|
26
|
-
}
|
|
27
4
|
|
|
28
5
|
interface ModuleHooks {
|
|
29
|
-
/**
|
|
30
|
-
* Hook to modify llms.txt content before final output
|
|
31
|
-
* Other modules can append their own API endpoints here
|
|
32
|
-
*/
|
|
33
|
-
'ai-ready:llms-txt': (payload: LlmsTxtGeneratePayload) => void | Promise<void>;
|
|
34
6
|
/**
|
|
35
7
|
* Hook to add routes to the AI ready
|
|
36
8
|
* Other modules can register their own API routes
|
|
@@ -58,5 +30,10 @@ interface ModulePublicRuntimeConfig {
|
|
|
58
30
|
}
|
|
59
31
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
60
32
|
|
|
33
|
+
declare module '@nuxt/schema' {
|
|
34
|
+
interface NuxtHooks extends ModuleHooks {
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
61
38
|
export { _default as default };
|
|
62
39
|
export type { ModuleHooks, ModulePublicRuntimeConfig };
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -6,26 +6,39 @@ import { TagIdMap } from 'mdream';
|
|
|
6
6
|
import { extractionPlugin } from 'mdream/plugins';
|
|
7
7
|
import { htmlToMarkdownSplitChunksStream } from 'mdream/splitter';
|
|
8
8
|
import { useSiteConfig, installNuxtSiteConfig, withSiteUrl } from 'nuxt-site-config/kit';
|
|
9
|
+
import { isPathFile } from 'nuxt-site-config/urls';
|
|
9
10
|
import { relative, resolve, dirname } from 'pathe';
|
|
10
11
|
import { readPackageJSON } from 'pkg-types';
|
|
11
12
|
import { estimateTokenCount } from 'tokenx';
|
|
12
|
-
import {
|
|
13
|
+
import { stat } from 'node:fs/promises';
|
|
13
14
|
import { join } from 'node:path';
|
|
14
|
-
import {
|
|
15
|
-
import { normalizeLlmsTxtConfig } from '../dist/runtime/llms-txt.js';
|
|
15
|
+
import { createLlmsTxtStream } from 'mdream/llms-txt';
|
|
16
16
|
|
|
17
17
|
const logger = useLogger("nuxt-ai-ready");
|
|
18
18
|
|
|
19
19
|
function setupPrerenderHandler() {
|
|
20
20
|
const nuxt = useNuxt();
|
|
21
|
-
const pages = [];
|
|
22
21
|
nuxt.hooks.hook("nitro:init", async (nitro) => {
|
|
22
|
+
let writer = null;
|
|
23
|
+
let pageCount = 0;
|
|
24
|
+
const startTime = Date.now();
|
|
23
25
|
nitro.hooks.hook("prerender:generate", async (route) => {
|
|
24
26
|
if (!route.fileName?.endsWith(".md")) {
|
|
25
27
|
return;
|
|
26
28
|
}
|
|
29
|
+
if (!writer) {
|
|
30
|
+
const siteConfig = useSiteConfig();
|
|
31
|
+
const stream = createLlmsTxtStream({
|
|
32
|
+
siteName: siteConfig.name || siteConfig.url,
|
|
33
|
+
description: siteConfig.description,
|
|
34
|
+
origin: siteConfig.url,
|
|
35
|
+
generateFull: true,
|
|
36
|
+
outputDir: nitro.options.output.publicDir
|
|
37
|
+
});
|
|
38
|
+
writer = stream.getWriter();
|
|
39
|
+
}
|
|
27
40
|
const { markdown, title, description } = JSON.parse(route.contents || "{}");
|
|
28
|
-
|
|
41
|
+
await writer.write({
|
|
29
42
|
filePath: route.fileName,
|
|
30
43
|
url: route.route,
|
|
31
44
|
title,
|
|
@@ -34,68 +47,37 @@ function setupPrerenderHandler() {
|
|
|
34
47
|
description,
|
|
35
48
|
title
|
|
36
49
|
}
|
|
37
|
-
};
|
|
38
|
-
|
|
50
|
+
});
|
|
51
|
+
pageCount++;
|
|
39
52
|
route.contents = markdown;
|
|
40
53
|
});
|
|
41
54
|
nitro.hooks.hook("prerender:done", async () => {
|
|
42
|
-
if (
|
|
55
|
+
if (!writer) {
|
|
43
56
|
return;
|
|
44
57
|
}
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
logger.success(`Generated markdown for ${pages.length} pages`);
|
|
55
|
-
const hookPayload = {
|
|
56
|
-
content: artifacts.llmsTxt || "",
|
|
57
|
-
fullContent: artifacts.llmsFullTxt || "",
|
|
58
|
-
pages
|
|
59
|
-
};
|
|
60
|
-
const llmsTxtConfig = nuxt.options.runtimeConfig["nuxt-ai-ready"].llmsTxt;
|
|
61
|
-
const normalizedContent = normalizeLlmsTxtConfig(llmsTxtConfig);
|
|
62
|
-
if (normalizedContent) {
|
|
63
|
-
hookPayload.content = `${hookPayload.content}
|
|
64
|
-
|
|
65
|
-
${normalizedContent}
|
|
66
|
-
`;
|
|
67
|
-
}
|
|
68
|
-
await nuxt.hooks.callHook("ai-ready:llms-txt", hookPayload);
|
|
69
|
-
const finalLlmsTxt = hookPayload.content;
|
|
70
|
-
const finalLlmsFullTxt = hookPayload.fullContent;
|
|
71
|
-
const generatedFiles = [];
|
|
72
|
-
if (finalLlmsTxt) {
|
|
73
|
-
const llmsTxtPath = join(nitro.options.output.publicDir, "llms.txt");
|
|
74
|
-
await writeFile(llmsTxtPath, finalLlmsTxt, "utf-8");
|
|
75
|
-
const sizeKb = (Buffer.byteLength(finalLlmsTxt, "utf-8") / 1024).toFixed(2);
|
|
76
|
-
generatedFiles.push({ path: "llms.txt", size: `${sizeKb}kb` });
|
|
77
|
-
nitro._prerenderedRoutes.push({
|
|
58
|
+
await writer.close();
|
|
59
|
+
const llmsTxtPath = join(nitro.options.output.publicDir, "llms.txt");
|
|
60
|
+
const llmsFullTxtPath = join(nitro.options.output.publicDir, "llms-full.txt");
|
|
61
|
+
const [llmsStats, llmsFullStats] = await Promise.all([
|
|
62
|
+
stat(llmsTxtPath),
|
|
63
|
+
stat(llmsFullTxtPath)
|
|
64
|
+
]);
|
|
65
|
+
nitro._prerenderedRoutes.push(
|
|
66
|
+
{
|
|
78
67
|
route: "/llms.txt",
|
|
79
68
|
fileName: llmsTxtPath,
|
|
80
69
|
generateTimeMS: 0
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (finalLlmsFullTxt) {
|
|
84
|
-
const llmsFullTxtPath = join(nitro.options.output.publicDir, "llms-full.txt");
|
|
85
|
-
await writeFile(llmsFullTxtPath, finalLlmsFullTxt, "utf-8");
|
|
86
|
-
const sizeKb = (Buffer.byteLength(finalLlmsFullTxt, "utf-8") / 1024).toFixed(2);
|
|
87
|
-
generatedFiles.push({ path: "llms-full.txt", size: `${sizeKb}kb` });
|
|
88
|
-
nitro._prerenderedRoutes.push({
|
|
70
|
+
},
|
|
71
|
+
{
|
|
89
72
|
route: "/llms-full.txt",
|
|
90
73
|
fileName: llmsFullTxtPath,
|
|
91
74
|
generateTimeMS: 0
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
const elapsed = Date.now() - startTime;
|
|
78
|
+
const llmsKb = (llmsStats.size / 1024).toFixed(2);
|
|
79
|
+
const llmsFullKb = (llmsFullStats.size / 1024).toFixed(2);
|
|
80
|
+
logger.info(`Generated llms.txt (${llmsKb}kb) and llms-full.txt (${llmsFullKb}kb) from ${pageCount} pages in ${elapsed}ms`);
|
|
99
81
|
});
|
|
100
82
|
});
|
|
101
83
|
}
|
|
@@ -114,16 +96,7 @@ const module = defineNuxtModule({
|
|
|
114
96
|
},
|
|
115
97
|
moduleDependencies: {
|
|
116
98
|
"@nuxtjs/robots": {
|
|
117
|
-
version: ">=5.6.0"
|
|
118
|
-
defaults: {
|
|
119
|
-
groups: [
|
|
120
|
-
{
|
|
121
|
-
userAgent: "*",
|
|
122
|
-
contentUsage: ["train-ai=y"],
|
|
123
|
-
contentSignal: ["ai-train=yes", "search=yes", "ai-input=yes"]
|
|
124
|
-
}
|
|
125
|
-
]
|
|
126
|
-
}
|
|
99
|
+
version: ">=5.6.0"
|
|
127
100
|
},
|
|
128
101
|
"nuxt-site-config": {
|
|
129
102
|
version: ">=3"
|
|
@@ -168,6 +141,13 @@ const module = defineNuxtModule({
|
|
|
168
141
|
resolve$1("./runtime/server/utils"),
|
|
169
142
|
resolve$1("./runtime/server/mcp")
|
|
170
143
|
);
|
|
144
|
+
if (typeof config.contentSignal === "object") {
|
|
145
|
+
nuxt.options.robots.groups.push({
|
|
146
|
+
userAgent: "*",
|
|
147
|
+
contentUsage: [`train-ai=${config.contentSignal.aiTrain ? "y" : "n"}`],
|
|
148
|
+
contentSignal: [`ai-train=${config.contentSignal.aiTrain ? "yes" : "no"}`, `search=${config.contentSignal.search ? "yes" : "no"}`, `ai-input=${config.contentSignal.aiInput ? "yes" : "no"}`]
|
|
149
|
+
});
|
|
150
|
+
}
|
|
171
151
|
addTypeTemplate({
|
|
172
152
|
filename: "module/nuxt-ai-ready.d.ts",
|
|
173
153
|
getContents: (data) => {
|
|
@@ -195,7 +175,7 @@ export {}
|
|
|
195
175
|
if (config.bulkRoute !== false) {
|
|
196
176
|
const resolvedBulkRoute = withSiteUrl(config.bulkRoute);
|
|
197
177
|
defaultLlmsTxtSections.push({
|
|
198
|
-
title: "
|
|
178
|
+
title: "LLM Tools",
|
|
199
179
|
links: [
|
|
200
180
|
{
|
|
201
181
|
title: "Bulk Data",
|
|
@@ -228,7 +208,7 @@ Returns JSONL (newline-delimited JSON) with all indexed content.`
|
|
|
228
208
|
defaultLlmsTxtSections[0].links.push(mcpLink);
|
|
229
209
|
} else {
|
|
230
210
|
defaultLlmsTxtSections.push({
|
|
231
|
-
title: "
|
|
211
|
+
title: "LLM Tools",
|
|
232
212
|
links: [mcpLink]
|
|
233
213
|
});
|
|
234
214
|
}
|
|
@@ -295,6 +275,9 @@ Returns JSONL (newline-delimited JSON) with all indexed content.`
|
|
|
295
275
|
if (typeof route._sitemap !== "undefined" && !route._sitemap) {
|
|
296
276
|
return;
|
|
297
277
|
}
|
|
278
|
+
if (isPathFile(route.route)) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
298
281
|
let title = "";
|
|
299
282
|
let description = "";
|
|
300
283
|
const headings = [];
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -44,6 +44,24 @@ export interface ModuleOptions {
|
|
|
44
44
|
* Structured llms.txt configuration
|
|
45
45
|
*/
|
|
46
46
|
llmsTxt?: LlmsTxtConfig;
|
|
47
|
+
/**
|
|
48
|
+
* Content Signal Directives
|
|
49
|
+
*/
|
|
50
|
+
contentSignal?: false | {
|
|
51
|
+
/**
|
|
52
|
+
* Allow Training or fine-tuning AI models.
|
|
53
|
+
*/
|
|
54
|
+
aiTrain?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Allow building a search index and providing search results (e.g., returning hyperlinks and short excerpts from your website's contents).
|
|
57
|
+
* Search does not include providing AI-generated search summaries.
|
|
58
|
+
*/
|
|
59
|
+
search?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Inputting content into one or more AI models (e.g., retrieval augmented generation, grounding, or other real-time taking of content for generative AI search answers).
|
|
62
|
+
*/
|
|
63
|
+
aiInput?: boolean;
|
|
64
|
+
};
|
|
47
65
|
}
|
|
48
66
|
/**
|
|
49
67
|
* Individual chunk entry in bulk.jsonl (one per chunk)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-ai-ready",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.1.2",
|
|
5
5
|
"description": "Best practice AI & LLM discoverability for Nuxt sites.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"@nuxt/kit": "4.2.1",
|
|
36
36
|
"consola": "^3.4.2",
|
|
37
37
|
"defu": "^6.1.4",
|
|
38
|
-
"mdream": "^0.
|
|
38
|
+
"mdream": "^0.15.0",
|
|
39
39
|
"minimatch": "^10.1.1",
|
|
40
40
|
"nuxt-site-config": "^3.2.11",
|
|
41
41
|
"pathe": "^2.0.3",
|
|
@@ -55,10 +55,10 @@
|
|
|
55
55
|
"@nuxtjs/color-mode": "^4.0.0",
|
|
56
56
|
"@nuxtjs/eslint-config-typescript": "^12.1.0",
|
|
57
57
|
"@nuxtjs/i18n": "^10.2.1",
|
|
58
|
-
"@nuxtjs/mcp-toolkit": "^0.
|
|
58
|
+
"@nuxtjs/mcp-toolkit": "^0.5.1",
|
|
59
59
|
"@nuxtjs/robots": "^5.6.0",
|
|
60
60
|
"@nuxtjs/sitemap": "^7.4.7",
|
|
61
|
-
"@vitest/coverage-v8": "^4.0.
|
|
61
|
+
"@vitest/coverage-v8": "^4.0.15",
|
|
62
62
|
"@vueuse/nuxt": "^14.1.0",
|
|
63
63
|
"better-sqlite3": "^12.5.0",
|
|
64
64
|
"bumpp": "^10.3.2",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"playwright-core": "^1.57.0",
|
|
72
72
|
"postgres": "^3.4.7",
|
|
73
73
|
"typescript": "^5.9.3",
|
|
74
|
-
"vitest": "^4.0.
|
|
74
|
+
"vitest": "^4.0.15",
|
|
75
75
|
"vue": "^3.5.25",
|
|
76
76
|
"vue-router": "^4.6.3",
|
|
77
77
|
"vue-tsc": "^3.1.5",
|