nuxt-ai-ready 0.6.2 → 0.7.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/README.md +1 -0
- package/dist/module.d.mts +12 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +226 -352
- package/dist/runtime/index.d.ts +5 -7
- package/dist/runtime/index.js +14 -3
- package/dist/runtime/llms-txt-format.d.ts +8 -0
- package/dist/runtime/llms-txt-format.js +32 -0
- package/dist/runtime/llms-txt-utils.d.ts +2 -5
- package/dist/runtime/llms-txt-utils.js +31 -88
- package/dist/runtime/server/db/index.d.ts +3 -7
- package/dist/runtime/server/db/index.js +14 -31
- package/dist/runtime/server/db/queries.d.ts +106 -26
- package/dist/runtime/server/db/queries.js +330 -33
- package/dist/runtime/server/db/schema-sql.d.ts +3 -0
- package/dist/runtime/server/db/schema-sql.js +71 -0
- package/dist/runtime/server/db/shared.d.ts +88 -0
- package/dist/runtime/server/db/shared.js +117 -0
- package/dist/runtime/server/mcp/resources/pages.js +11 -3
- package/dist/runtime/server/mcp/tools/list-pages.js +33 -7
- package/dist/runtime/server/mcp/tools/search-pages.js +3 -3
- package/dist/runtime/server/middleware/markdown.js +2 -3
- package/dist/runtime/server/middleware/markdown.prerender.js +6 -5
- package/dist/runtime/server/plugins/db-restore.js +10 -8
- package/dist/runtime/server/plugins/sitemap-seeder.js +44 -0
- package/dist/runtime/server/routes/__ai-ready/indexnow.post.d.ts +2 -0
- package/dist/runtime/server/routes/__ai-ready/indexnow.post.js +18 -0
- package/dist/runtime/server/routes/__ai-ready/poll.post.d.ts +8 -0
- package/dist/runtime/server/routes/__ai-ready/poll.post.js +25 -0
- package/dist/runtime/server/routes/__ai-ready/prune.post.d.ts +14 -0
- package/dist/runtime/server/routes/__ai-ready/prune.post.js +21 -0
- package/dist/runtime/server/routes/__ai-ready/status.get.d.ts +2 -0
- package/dist/runtime/server/routes/__ai-ready/status.get.js +28 -0
- package/dist/runtime/server/routes/__ai-ready-debug.get.js +13 -14
- package/dist/runtime/server/routes/indexnow-key.get.d.ts +6 -0
- package/dist/runtime/server/routes/indexnow-key.get.js +10 -0
- package/dist/runtime/server/routes/llms-full.txt.get.d.ts +1 -1
- package/dist/runtime/server/routes/llms-full.txt.get.js +34 -3
- package/dist/runtime/server/tasks/ai-ready-index.d.ts +11 -0
- package/dist/runtime/server/tasks/ai-ready-index.js +39 -0
- package/dist/runtime/server/utils/batchIndex.d.ts +26 -0
- package/dist/runtime/server/utils/batchIndex.js +53 -0
- package/dist/runtime/server/utils/indexPage.d.ts +8 -2
- package/dist/runtime/server/utils/indexPage.js +38 -23
- package/dist/runtime/server/utils/indexnow.d.ts +27 -0
- package/dist/runtime/server/utils/indexnow.js +66 -0
- package/dist/runtime/server/utils/keywords.d.ts +0 -4
- package/dist/runtime/server/utils/keywords.js +0 -3
- package/dist/runtime/server/utils/llms-full.d.ts +11 -0
- package/dist/runtime/server/utils/llms-full.js +39 -0
- package/dist/runtime/server/utils/sitemap.js +3 -3
- package/dist/runtime/server/utils.d.ts +12 -10
- package/dist/runtime/server/utils.js +63 -94
- package/dist/runtime/types.d.ts +64 -40
- package/package.json +28 -29
- package/dist/runtime/mcp.d.ts +0 -32
- package/dist/runtime/mcp.js +0 -5
- package/dist/runtime/server/db/dump.d.ts +0 -29
- package/dist/runtime/server/db/dump.js +0 -29
- package/dist/runtime/server/db/schema.d.ts +0 -8
- package/dist/runtime/server/db/schema.js +0 -124
- package/dist/runtime/server/plugins/page-indexer.js +0 -68
- package/dist/runtime/server/tsconfig.json +0 -3
- package/dist/runtime/server/utils/pageData.d.ts +0 -28
- package/dist/runtime/server/utils/pageData.js +0 -43
- package/mcp.d.ts +0 -4
- /package/dist/runtime/{nuxt → app}/plugins/md-hints.prerender.d.ts +0 -0
- /package/dist/runtime/{nuxt → app}/plugins/md-hints.prerender.js +0 -0
- /package/dist/runtime/server/plugins/{page-indexer.d.ts → sitemap-seeder.d.ts} +0 -0
package/dist/module.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { join
|
|
3
|
-
import { useLogger, useNuxt, defineNuxtModule, createResolver,
|
|
1
|
+
import { mkdir, writeFile, appendFile, stat, access } from 'node:fs/promises';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { useLogger, useNuxt, addTypeTemplate, defineNuxtModule, createResolver, hasNuxtModule, addServerHandler, addPlugin } from '@nuxt/kit';
|
|
4
4
|
import defu from 'defu';
|
|
5
5
|
import { useSiteConfig, installNuxtSiteConfig, withSiteUrl } from 'nuxt-site-config/kit';
|
|
6
6
|
import { readPackageJSON } from 'pkg-types';
|
|
@@ -10,7 +10,9 @@ import { isTest, isCI } from 'std-env';
|
|
|
10
10
|
import { parseSitemapXml } from '@nuxtjs/sitemap/utils';
|
|
11
11
|
import { colorize } from 'consola/utils';
|
|
12
12
|
import { withBase } from 'ufo';
|
|
13
|
-
import {
|
|
13
|
+
import { createAdapter, initSchema, computeContentHash, insertPage, queryAllPages, exportDbDump } from '../dist/runtime/server/db/shared.js';
|
|
14
|
+
import { buildLlmsFullTxtHeader, formatPageForLlmsFullTxt } from '../dist/runtime/server/utils/llms-full.js';
|
|
15
|
+
import { join as join$1, isAbsolute } from 'pathe';
|
|
14
16
|
|
|
15
17
|
const logger = useLogger("nuxt-ai-ready");
|
|
16
18
|
|
|
@@ -19,7 +21,7 @@ function hookNuxtSeoProLicense() {
|
|
|
19
21
|
const isBuild = !nuxt.options.dev && !nuxt.options._prepare;
|
|
20
22
|
if (isBuild && !nuxt._isNuxtSeoProVerifying) {
|
|
21
23
|
const license = nuxt.options.runtimeConfig.seoProKey || process.env.NUXT_SEO_PRO_KEY;
|
|
22
|
-
if (isTest) {
|
|
24
|
+
if (isTest || process.env.VITEST) {
|
|
23
25
|
return;
|
|
24
26
|
}
|
|
25
27
|
if (!isCI && !license) {
|
|
@@ -65,118 +67,28 @@ function hookNuxtSeoProLicense() {
|
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
const connectors = {
|
|
70
|
-
d1: "db0/connectors/cloudflare-d1",
|
|
71
|
-
libsql: "db0/connectors/libsql/node"
|
|
72
|
-
};
|
|
73
|
-
if (type !== "sqlite" && connectors[type]) {
|
|
74
|
-
return connectors[type];
|
|
75
|
-
}
|
|
76
|
-
if (process.versions.bun) {
|
|
77
|
-
return "db0/connectors/bun-sqlite";
|
|
78
|
-
}
|
|
79
|
-
const nodeVersion = Number.parseInt(process.versions.node?.split(".")[0] || "0");
|
|
80
|
-
if (nodeVersion >= 22) {
|
|
81
|
-
return "db0/connectors/node-sqlite";
|
|
82
|
-
}
|
|
83
|
-
return "db0/connectors/better-sqlite3";
|
|
84
|
-
}
|
|
85
|
-
function refineDatabaseConfig(config, rootDir) {
|
|
86
|
-
const type = config.type || "sqlite";
|
|
87
|
-
if (type === "sqlite") {
|
|
88
|
-
const filename = config.filename || ".data/ai-ready/pages.db";
|
|
89
|
-
return {
|
|
90
|
-
type: "sqlite",
|
|
91
|
-
filename: isAbsolute(filename) ? filename : join(rootDir, filename)
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
if (type === "d1") {
|
|
95
|
-
return {
|
|
96
|
-
type: "d1",
|
|
97
|
-
bindingName: config.bindingName || "AI_READY_DB"
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
return {
|
|
101
|
-
type: "libsql",
|
|
102
|
-
url: config.url,
|
|
103
|
-
authToken: config.authToken
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function normalizeLink(link) {
|
|
108
|
-
const parts = [];
|
|
109
|
-
parts.push(`- [${link.title}](${link.href})`);
|
|
110
|
-
if (link.description)
|
|
111
|
-
parts.push(` ${link.description}`);
|
|
112
|
-
return parts.join("\n");
|
|
113
|
-
}
|
|
114
|
-
function normalizeSection(section) {
|
|
115
|
-
const parts = [];
|
|
116
|
-
parts.push(`## ${section.title}`);
|
|
117
|
-
parts.push("");
|
|
118
|
-
if (section.description) {
|
|
119
|
-
const descriptions = Array.isArray(section.description) ? section.description : [section.description];
|
|
120
|
-
parts.push(...descriptions);
|
|
121
|
-
parts.push("");
|
|
122
|
-
}
|
|
123
|
-
if (section.links?.length)
|
|
124
|
-
parts.push(...section.links.map(normalizeLink));
|
|
125
|
-
return parts.join("\n");
|
|
126
|
-
}
|
|
127
|
-
function normalizeLlmsTxtConfig(config) {
|
|
128
|
-
const parts = [];
|
|
129
|
-
if (config.sections?.length)
|
|
130
|
-
parts.push(...config.sections.map(normalizeSection));
|
|
131
|
-
if (config.notes) {
|
|
132
|
-
parts.push("## Notes");
|
|
133
|
-
parts.push("");
|
|
134
|
-
const notes = Array.isArray(config.notes) ? config.notes : [config.notes];
|
|
135
|
-
parts.push(...notes);
|
|
136
|
-
}
|
|
137
|
-
return parts.join("\n\n");
|
|
138
|
-
}
|
|
139
|
-
function createCrawlerState(pageDataPath, llmsFullTxtPath, siteInfo, llmsTxtConfig) {
|
|
70
|
+
function createCrawlerState(dbPath, llmsFullTxtPath, siteInfo, llmsTxtConfig) {
|
|
140
71
|
return {
|
|
141
72
|
prerenderedRoutes: /* @__PURE__ */ new Set(),
|
|
142
73
|
errorRoutes: /* @__PURE__ */ new Set(),
|
|
143
74
|
totalProcessingTime: 0,
|
|
144
75
|
initialized: false,
|
|
145
|
-
|
|
146
|
-
pageDataPath,
|
|
76
|
+
dbPath,
|
|
147
77
|
llmsFullTxtPath,
|
|
148
78
|
siteInfo,
|
|
149
79
|
llmsTxtConfig
|
|
150
80
|
};
|
|
151
81
|
}
|
|
152
|
-
function buildLlmsFullTxtHeader(siteInfo, llmsTxtConfig) {
|
|
153
|
-
const parts = [];
|
|
154
|
-
parts.push(`# ${siteInfo?.name || siteInfo?.url || "Site"}`);
|
|
155
|
-
if (siteInfo?.description)
|
|
156
|
-
parts.push(`
|
|
157
|
-
> ${siteInfo.description}`);
|
|
158
|
-
if (siteInfo?.url)
|
|
159
|
-
parts.push(`
|
|
160
|
-
Canonical Origin: ${siteInfo.url}`);
|
|
161
|
-
parts.push("");
|
|
162
|
-
if (llmsTxtConfig) {
|
|
163
|
-
const normalizedContent = normalizeLlmsTxtConfig(llmsTxtConfig);
|
|
164
|
-
if (normalizedContent) {
|
|
165
|
-
parts.push(normalizedContent);
|
|
166
|
-
parts.push("");
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
parts.push("## Pages\n\n");
|
|
170
|
-
return parts.join("\n");
|
|
171
|
-
}
|
|
172
82
|
async function initCrawler(state) {
|
|
173
83
|
if (state.initialized)
|
|
174
84
|
return;
|
|
175
|
-
if (state.
|
|
176
|
-
await mkdir(dirname(state.
|
|
177
|
-
await
|
|
178
|
-
state.
|
|
179
|
-
|
|
85
|
+
if (state.dbPath) {
|
|
86
|
+
await mkdir(dirname(state.dbPath), { recursive: true });
|
|
87
|
+
const { default: betterSqlite3 } = await import('db0/connectors/better-sqlite3');
|
|
88
|
+
const connector = betterSqlite3({ path: state.dbPath });
|
|
89
|
+
state.db = createAdapter(connector);
|
|
90
|
+
await initSchema(state.db);
|
|
91
|
+
logger.debug(`Crawler initialized with SQLite at ${state.dbPath}`);
|
|
180
92
|
}
|
|
181
93
|
if (state.llmsFullTxtPath) {
|
|
182
94
|
await mkdir(dirname(state.llmsFullTxtPath), { recursive: true });
|
|
@@ -189,34 +101,6 @@ async function initCrawler(state) {
|
|
|
189
101
|
function flattenHeadings(headings) {
|
|
190
102
|
return (headings || []).map((h) => Object.entries(h).map(([tag, text]) => `${tag}:${text}`).join("")).join("|");
|
|
191
103
|
}
|
|
192
|
-
function stripFrontmatter(markdown) {
|
|
193
|
-
return markdown.replace(/^---\n[\s\S]*?\n---\n*/, "");
|
|
194
|
-
}
|
|
195
|
-
function normalizeHeadings(markdown) {
|
|
196
|
-
return markdown.replace(/^(#{1,6})\s+(.+)$/gm, (_, hashes, text) => {
|
|
197
|
-
const level = hashes.length;
|
|
198
|
-
return `h${level}. ${text}`;
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
function formatPageForLlmsFullTxt(route, title, description, markdown, siteUrl) {
|
|
202
|
-
const canonicalUrl = siteUrl ? `${siteUrl.replace(/\/$/, "")}${route}` : route;
|
|
203
|
-
const heading = title && title !== route ? `### ${title}` : `### ${route}`;
|
|
204
|
-
let content = stripFrontmatter(markdown);
|
|
205
|
-
content = normalizeHeadings(content);
|
|
206
|
-
const parts = [heading, ""];
|
|
207
|
-
parts.push(`Source: ${canonicalUrl}`);
|
|
208
|
-
if (description)
|
|
209
|
-
parts.push(`Description: ${description}`);
|
|
210
|
-
parts.push("");
|
|
211
|
-
if (content.trim()) {
|
|
212
|
-
parts.push(content.trim());
|
|
213
|
-
parts.push("");
|
|
214
|
-
}
|
|
215
|
-
parts.push("---");
|
|
216
|
-
parts.push("");
|
|
217
|
-
return `${parts.join("\n")}
|
|
218
|
-
`;
|
|
219
|
-
}
|
|
220
104
|
async function processMarkdownRoute(state, nuxt, route, parsed, lastmod, options) {
|
|
221
105
|
const { markdown, title, description, headings, keywords, updatedAt: metaUpdatedAt } = parsed;
|
|
222
106
|
let updatedAt = (lastmod instanceof Date ? lastmod.toISOString() : lastmod) || (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -226,18 +110,22 @@ async function processMarkdownRoute(state, nuxt, route, parsed, lastmod, options
|
|
|
226
110
|
updatedAt = parsedDate.toISOString();
|
|
227
111
|
}
|
|
228
112
|
await nuxt.hooks.callHook("ai-ready:page:markdown", { route, markdown, title, description, headings });
|
|
229
|
-
if (state.
|
|
230
|
-
const
|
|
113
|
+
if (state.db) {
|
|
114
|
+
const contentHash = await computeContentHash(markdown);
|
|
115
|
+
await insertPage(state.db, {
|
|
231
116
|
route,
|
|
232
117
|
title,
|
|
233
118
|
description,
|
|
119
|
+
markdown,
|
|
234
120
|
headings: flattenHeadings(headings),
|
|
235
121
|
keywords: keywords || [],
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
|
|
122
|
+
contentHash,
|
|
123
|
+
updatedAt
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
if (state.llmsFullTxtPath && !options?.skipLlmsFullTxt) {
|
|
127
|
+
const pageContent = formatPageForLlmsFullTxt(route, title, description, markdown, state.siteInfo?.url);
|
|
128
|
+
await appendFile(state.llmsFullTxtPath, pageContent, "utf-8");
|
|
241
129
|
}
|
|
242
130
|
state.prerenderedRoutes.add(route);
|
|
243
131
|
}
|
|
@@ -275,7 +163,7 @@ async function crawlSitemapEntries(state, nuxt, nitro, entries) {
|
|
|
275
163
|
logger.debug(`Skipping ${route}: Response is not JSON (likely HTML instead of markdown conversion)`, err);
|
|
276
164
|
continue;
|
|
277
165
|
}
|
|
278
|
-
await processMarkdownRoute(state, nuxt, route, parsed, lastmod);
|
|
166
|
+
await processMarkdownRoute(state, nuxt, route, parsed, lastmod, { skipLlmsFullTxt: true });
|
|
279
167
|
crawled++;
|
|
280
168
|
}
|
|
281
169
|
logger.debug(`Sitemap crawl complete: ${crawled} crawled, ${skipped} skipped`);
|
|
@@ -314,92 +202,6 @@ function detectSitemapPrerender(sitemapName = "sitemap.xml") {
|
|
|
314
202
|
usePrerenderHook: shouldHookIntoPrerender && !prerenderSitemap
|
|
315
203
|
};
|
|
316
204
|
}
|
|
317
|
-
async function createDatabaseDump(entries, errorRoutes, dbConfig) {
|
|
318
|
-
const Database = (await import('better-sqlite3')).default;
|
|
319
|
-
const dbPath = dbConfig.filename || ".data/ai-ready/pages.db";
|
|
320
|
-
await mkdir(dirname(dbPath), { recursive: true });
|
|
321
|
-
const db = new Database(dbPath);
|
|
322
|
-
db.exec(`
|
|
323
|
-
CREATE TABLE IF NOT EXISTS pages (
|
|
324
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
325
|
-
route TEXT UNIQUE NOT NULL,
|
|
326
|
-
route_key TEXT UNIQUE NOT NULL,
|
|
327
|
-
title TEXT NOT NULL DEFAULT '',
|
|
328
|
-
description TEXT NOT NULL DEFAULT '',
|
|
329
|
-
markdown TEXT NOT NULL DEFAULT '',
|
|
330
|
-
headings TEXT NOT NULL DEFAULT '[]',
|
|
331
|
-
keywords TEXT NOT NULL DEFAULT '[]',
|
|
332
|
-
updated_at TEXT NOT NULL,
|
|
333
|
-
indexed_at INTEGER NOT NULL,
|
|
334
|
-
is_error INTEGER NOT NULL DEFAULT 0
|
|
335
|
-
);
|
|
336
|
-
CREATE INDEX IF NOT EXISTS idx_pages_route ON pages(route);
|
|
337
|
-
CREATE INDEX IF NOT EXISTS idx_pages_is_error ON pages(is_error);
|
|
338
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS pages_fts USING fts5(
|
|
339
|
-
route, title, description, markdown, headings, keywords,
|
|
340
|
-
content=pages, content_rowid=id
|
|
341
|
-
);
|
|
342
|
-
CREATE TRIGGER IF NOT EXISTS pages_ai AFTER INSERT ON pages BEGIN
|
|
343
|
-
INSERT INTO pages_fts(rowid, route, title, description, markdown, headings, keywords)
|
|
344
|
-
VALUES (new.id, new.route, new.title, new.description, new.markdown, new.headings, new.keywords);
|
|
345
|
-
END;
|
|
346
|
-
CREATE TRIGGER IF NOT EXISTS pages_ad AFTER DELETE ON pages BEGIN
|
|
347
|
-
INSERT INTO pages_fts(pages_fts, rowid, route, title, description, markdown, headings, keywords)
|
|
348
|
-
VALUES('delete', old.id, old.route, old.title, old.description, old.markdown, old.headings, old.keywords);
|
|
349
|
-
END;
|
|
350
|
-
CREATE TRIGGER IF NOT EXISTS pages_au AFTER UPDATE ON pages BEGIN
|
|
351
|
-
INSERT INTO pages_fts(pages_fts, rowid, route, title, description, markdown, headings, keywords)
|
|
352
|
-
VALUES('delete', old.id, old.route, old.title, old.description, old.markdown, old.headings, old.keywords);
|
|
353
|
-
INSERT INTO pages_fts(rowid, route, title, description, markdown, headings, keywords)
|
|
354
|
-
VALUES (new.id, new.route, new.title, new.description, new.markdown, new.headings, new.keywords);
|
|
355
|
-
END;
|
|
356
|
-
`);
|
|
357
|
-
const insertStmt = db.prepare(`
|
|
358
|
-
INSERT OR REPLACE INTO pages (route, route_key, title, description, markdown, headings, keywords, updated_at, indexed_at, is_error)
|
|
359
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
360
|
-
`);
|
|
361
|
-
const normalizeRouteKey = (route) => route.replace(/^\//, "").replace(/\//g, ":") || "index";
|
|
362
|
-
const now = Date.now();
|
|
363
|
-
for (const entry of entries) {
|
|
364
|
-
insertStmt.run(
|
|
365
|
-
entry.route,
|
|
366
|
-
normalizeRouteKey(entry.route),
|
|
367
|
-
entry.title,
|
|
368
|
-
entry.description,
|
|
369
|
-
entry.markdown,
|
|
370
|
-
entry.headings,
|
|
371
|
-
JSON.stringify(entry.keywords),
|
|
372
|
-
entry.updatedAt,
|
|
373
|
-
now,
|
|
374
|
-
0
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
for (const route of errorRoutes) {
|
|
378
|
-
insertStmt.run(
|
|
379
|
-
route,
|
|
380
|
-
normalizeRouteKey(route),
|
|
381
|
-
"",
|
|
382
|
-
"",
|
|
383
|
-
"",
|
|
384
|
-
"[]",
|
|
385
|
-
"[]",
|
|
386
|
-
(/* @__PURE__ */ new Date()).toISOString(),
|
|
387
|
-
now,
|
|
388
|
-
1
|
|
389
|
-
);
|
|
390
|
-
}
|
|
391
|
-
const rows = db.prepare(`
|
|
392
|
-
SELECT route, route_key, title, description, markdown, headings, keywords, updated_at, indexed_at, is_error
|
|
393
|
-
FROM pages
|
|
394
|
-
`).all();
|
|
395
|
-
db.close();
|
|
396
|
-
const json = JSON.stringify(rows);
|
|
397
|
-
const encoder = new TextEncoder();
|
|
398
|
-
const stream = new Blob([encoder.encode(json)]).stream();
|
|
399
|
-
const compressed = stream.pipeThrough(new CompressionStream("gzip"));
|
|
400
|
-
const buffer = await new Response(compressed).arrayBuffer();
|
|
401
|
-
return Buffer.from(buffer).toString("base64");
|
|
402
|
-
}
|
|
403
205
|
async function prerenderRoute(nitro, route) {
|
|
404
206
|
const start = Date.now();
|
|
405
207
|
const encodedRoute = encodeURI(route);
|
|
@@ -409,7 +211,7 @@ async function prerenderRoute(nitro, route) {
|
|
|
409
211
|
retry: nitro.options.prerender.retry,
|
|
410
212
|
retryDelay: nitro.options.prerender.retryDelay
|
|
411
213
|
});
|
|
412
|
-
const filePath = join
|
|
214
|
+
const filePath = join(nitro.options.output.publicDir, route);
|
|
413
215
|
await mkdir(dirname(filePath), { recursive: true });
|
|
414
216
|
const data = res._data;
|
|
415
217
|
if (data === void 0)
|
|
@@ -423,12 +225,11 @@ async function prerenderRoute(nitro, route) {
|
|
|
423
225
|
nitro._prerenderedRoutes.push(_route);
|
|
424
226
|
return stat(filePath);
|
|
425
227
|
}
|
|
426
|
-
function setupPrerenderHandler(
|
|
228
|
+
function setupPrerenderHandler(dbPath, siteInfo, llmsTxtConfig) {
|
|
427
229
|
const nuxt = useNuxt();
|
|
428
|
-
const dbConfig = refineDatabaseConfig({}, nuxt.options.rootDir);
|
|
429
230
|
nuxt.hooks.hook("nitro:init", async (nitro) => {
|
|
430
|
-
const llmsFullTxtPath = join
|
|
431
|
-
const state = createCrawlerState(
|
|
231
|
+
const llmsFullTxtPath = join(nitro.options.output.publicDir, "llms-full.txt");
|
|
232
|
+
const state = createCrawlerState(dbPath, llmsFullTxtPath, siteInfo, llmsTxtConfig);
|
|
432
233
|
let initPromise = null;
|
|
433
234
|
nitro.hooks.hook("prerender:generate", async (route) => {
|
|
434
235
|
if (route.error) {
|
|
@@ -447,76 +248,49 @@ function setupPrerenderHandler(pageDataPath, siteInfo, llmsTxtConfig) {
|
|
|
447
248
|
initPromise = initCrawler(state);
|
|
448
249
|
await initPromise;
|
|
449
250
|
const parsed = JSON.parse(route.contents || "{}");
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
if (metaUpdatedAt) {
|
|
453
|
-
const parsedDate = new Date(metaUpdatedAt);
|
|
454
|
-
if (!Number.isNaN(parsedDate.getTime()))
|
|
455
|
-
updatedAt = parsedDate.toISOString();
|
|
456
|
-
}
|
|
457
|
-
await nuxt.hooks.callHook("ai-ready:page:markdown", {
|
|
458
|
-
route: pageRoute,
|
|
459
|
-
markdown,
|
|
460
|
-
title,
|
|
461
|
-
description,
|
|
462
|
-
headings
|
|
463
|
-
});
|
|
464
|
-
if (state.jsonlInitialized && state.pageDataPath) {
|
|
465
|
-
const pageData = {
|
|
466
|
-
route: pageRoute,
|
|
467
|
-
title,
|
|
468
|
-
description,
|
|
469
|
-
headings: flattenHeadings(headings),
|
|
470
|
-
keywords: keywords || [],
|
|
471
|
-
updatedAt,
|
|
472
|
-
markdown
|
|
473
|
-
};
|
|
474
|
-
await appendFile(state.pageDataPath, `${JSON.stringify(pageData)}
|
|
475
|
-
`, "utf-8");
|
|
476
|
-
}
|
|
477
|
-
if (state.llmsFullTxtPath) {
|
|
478
|
-
const pageContent = formatPageForLlmsFullTxt(pageRoute, title, description, markdown, state.siteInfo?.url);
|
|
479
|
-
await appendFile(state.llmsFullTxtPath, pageContent, "utf-8");
|
|
480
|
-
}
|
|
481
|
-
state.prerenderedRoutes.add(pageRoute);
|
|
482
|
-
route.contents = markdown;
|
|
251
|
+
await processMarkdownRoute(state, nuxt, pageRoute, parsed);
|
|
252
|
+
route.contents = parsed.markdown;
|
|
483
253
|
state.totalProcessingTime += Date.now() - pageStartTime;
|
|
484
254
|
});
|
|
485
255
|
async function writeLlmsFiles() {
|
|
486
|
-
if (state.
|
|
256
|
+
if (state.db && state.errorRoutes.size > 0) {
|
|
487
257
|
for (const route of state.errorRoutes) {
|
|
488
|
-
await
|
|
489
|
-
|
|
258
|
+
await insertPage(state.db, {
|
|
259
|
+
route,
|
|
260
|
+
title: "",
|
|
261
|
+
description: "",
|
|
262
|
+
markdown: "",
|
|
263
|
+
headings: "",
|
|
264
|
+
keywords: [],
|
|
265
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
266
|
+
isError: true
|
|
267
|
+
});
|
|
490
268
|
}
|
|
491
|
-
logger.debug(`Wrote ${state.errorRoutes.size} error routes to
|
|
269
|
+
logger.debug(`Wrote ${state.errorRoutes.size} error routes to database`);
|
|
492
270
|
}
|
|
493
|
-
const publicDataDir = join
|
|
271
|
+
const publicDataDir = join(nitro.options.output.publicDir, "__ai-ready");
|
|
494
272
|
await mkdir(publicDataDir, { recursive: true });
|
|
495
|
-
if (state.
|
|
496
|
-
const
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
const dumpPath = join$1(publicDataDir, "pages.dump");
|
|
517
|
-
await writeFile(dumpPath, dumpData, "utf-8");
|
|
518
|
-
logger.debug(`Created database dump at __ai-ready/pages.dump (${(dumpData.length / 1024).toFixed(1)}kb compressed)`);
|
|
519
|
-
}
|
|
273
|
+
if (state.db) {
|
|
274
|
+
const pages = await queryAllPages(state.db);
|
|
275
|
+
const errorRoutesList = (await queryAllPages(state.db, { includeErrors: true })).filter((p) => p.isError).map((p) => p.route);
|
|
276
|
+
const jsonContent = JSON.stringify({
|
|
277
|
+
pages: pages.map((p) => ({
|
|
278
|
+
route: p.route,
|
|
279
|
+
title: p.title,
|
|
280
|
+
description: p.description,
|
|
281
|
+
headings: p.headings,
|
|
282
|
+
keywords: p.keywords || [],
|
|
283
|
+
updatedAt: p.updatedAt
|
|
284
|
+
})),
|
|
285
|
+
errorRoutes: errorRoutesList
|
|
286
|
+
});
|
|
287
|
+
const publicJsonPath = join(publicDataDir, "pages.json");
|
|
288
|
+
await writeFile(publicJsonPath, jsonContent, "utf-8");
|
|
289
|
+
logger.debug(`Wrote ${pages.length} pages to __ai-ready/pages.json`);
|
|
290
|
+
const dumpData = await exportDbDump(state.db);
|
|
291
|
+
const dumpPath = join(publicDataDir, "pages.dump");
|
|
292
|
+
await writeFile(dumpPath, dumpData, "utf-8");
|
|
293
|
+
logger.debug(`Created database dump at __ai-ready/pages.dump (${(dumpData.length / 1024).toFixed(1)}kb compressed)`);
|
|
520
294
|
}
|
|
521
295
|
const llmsStats = await prerenderRoute(nitro, "/llms.txt");
|
|
522
296
|
const llmsFullStats = await stat(state.llmsFullTxtPath);
|
|
@@ -555,6 +329,90 @@ function setupPrerenderHandler(pageDataPath, siteInfo, llmsTxtConfig) {
|
|
|
555
329
|
});
|
|
556
330
|
}
|
|
557
331
|
|
|
332
|
+
function registerTypeTemplates(_ctx) {
|
|
333
|
+
addTypeTemplate({
|
|
334
|
+
filename: "types/nuxt-ai-ready.d.ts",
|
|
335
|
+
getContents: () => `// Generated by nuxt-ai-ready
|
|
336
|
+
import type { MarkdownContext, PageIndexedContext } from 'nuxt-ai-ready'
|
|
337
|
+
import type { HTMLToMarkdownOptions } from 'mdream'
|
|
338
|
+
|
|
339
|
+
declare module 'nitropack/types' {
|
|
340
|
+
interface NitroRuntimeHooks {
|
|
341
|
+
'ai-ready:markdown': (context: MarkdownContext) => void | Promise<void>
|
|
342
|
+
'ai-ready:mdreamConfig': (config: HTMLToMarkdownOptions) => void | Promise<void>
|
|
343
|
+
'ai-ready:page:indexed': (context: PageIndexedContext) => void | Promise<void>
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
declare module '#ai-ready-virtual/read-page-data.mjs' {
|
|
348
|
+
export function readPageDataFromFilesystem(): Promise<{
|
|
349
|
+
pages: Array<{
|
|
350
|
+
route: string
|
|
351
|
+
title: string
|
|
352
|
+
description: string
|
|
353
|
+
headings: string
|
|
354
|
+
keywords: string[]
|
|
355
|
+
updatedAt: string
|
|
356
|
+
markdown: string
|
|
357
|
+
}>
|
|
358
|
+
errorRoutes: string[]
|
|
359
|
+
}>
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
declare module '#ai-ready-virtual/page-data.mjs' {
|
|
363
|
+
export const pages: never[]
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
declare module '#ai-ready/adapter' {
|
|
367
|
+
import type { Connector } from 'db0'
|
|
368
|
+
const connector: (config: unknown) => Connector
|
|
369
|
+
export default connector
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
export {}
|
|
373
|
+
`
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async function resolveDatabaseAdapter(type) {
|
|
378
|
+
const connectors = {
|
|
379
|
+
d1: "db0/connectors/cloudflare-d1",
|
|
380
|
+
libsql: "db0/connectors/libsql/node"
|
|
381
|
+
};
|
|
382
|
+
if (type !== "sqlite" && connectors[type]) {
|
|
383
|
+
return connectors[type];
|
|
384
|
+
}
|
|
385
|
+
if (process.versions.bun) {
|
|
386
|
+
return "db0/connectors/bun-sqlite";
|
|
387
|
+
}
|
|
388
|
+
const nodeVersion = Number.parseInt(process.versions.node?.split(".")[0] || "0");
|
|
389
|
+
if (nodeVersion >= 22) {
|
|
390
|
+
return "db0/connectors/node-sqlite";
|
|
391
|
+
}
|
|
392
|
+
return "db0/connectors/better-sqlite3";
|
|
393
|
+
}
|
|
394
|
+
function refineDatabaseConfig(config, rootDir) {
|
|
395
|
+
const type = config.type || "sqlite";
|
|
396
|
+
if (type === "sqlite") {
|
|
397
|
+
const filename = config.filename || ".data/ai-ready/pages.db";
|
|
398
|
+
return {
|
|
399
|
+
type: "sqlite",
|
|
400
|
+
filename: isAbsolute(filename) ? filename : join$1(rootDir, filename)
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
if (type === "d1") {
|
|
404
|
+
return {
|
|
405
|
+
type: "d1",
|
|
406
|
+
bindingName: config.bindingName || "AI_READY_DB"
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
return {
|
|
410
|
+
type: "libsql",
|
|
411
|
+
url: config.url,
|
|
412
|
+
authToken: config.authToken
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
558
416
|
const module$1 = defineNuxtModule({
|
|
559
417
|
meta: {
|
|
560
418
|
name: "nuxt-ai-ready",
|
|
@@ -606,7 +464,6 @@ const module$1 = defineNuxtModule({
|
|
|
606
464
|
hookNuxtSeoProLicense();
|
|
607
465
|
nuxt.options.nitro.alias = nuxt.options.nitro.alias || {};
|
|
608
466
|
nuxt.options.alias["#ai-ready"] = resolve("./runtime");
|
|
609
|
-
({ resolver: createResolver(import.meta.url) });
|
|
610
467
|
const dbType = config.database?.type || "sqlite";
|
|
611
468
|
const adapterPath = await resolveDatabaseAdapter(dbType);
|
|
612
469
|
nuxt.options.alias["#ai-ready/adapter"] = adapterPath;
|
|
@@ -628,48 +485,7 @@ const module$1 = defineNuxtModule({
|
|
|
628
485
|
contentSignal: [`ai-train=${config.contentSignal.aiTrain ? "yes" : "no"}`, `search=${config.contentSignal.search ? "yes" : "no"}`, `ai-input=${config.contentSignal.aiInput ? "yes" : "no"}`]
|
|
629
486
|
});
|
|
630
487
|
}
|
|
631
|
-
|
|
632
|
-
filename: "types/nuxt-ai-ready.d.ts",
|
|
633
|
-
getContents: () => `// Generated by nuxt-ai-ready
|
|
634
|
-
import type { MarkdownContext, PageIndexedContext } from 'nuxt-ai-ready'
|
|
635
|
-
import type { HTMLToMarkdownOptions } from 'mdream'
|
|
636
|
-
|
|
637
|
-
declare module 'nitropack/types' {
|
|
638
|
-
interface NitroRuntimeHooks {
|
|
639
|
-
'ai-ready:markdown': (context: MarkdownContext) => void | Promise<void>
|
|
640
|
-
'ai-ready:mdreamConfig': (config: HTMLToMarkdownOptions) => void | Promise<void>
|
|
641
|
-
'ai-ready:page:indexed': (context: PageIndexedContext) => void | Promise<void>
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
declare module '#ai-ready-virtual/read-page-data.mjs' {
|
|
646
|
-
export function readPageDataFromFilesystem(): Promise<{
|
|
647
|
-
pages: Array<{
|
|
648
|
-
route: string
|
|
649
|
-
title: string
|
|
650
|
-
description: string
|
|
651
|
-
headings: string
|
|
652
|
-
keywords: string[]
|
|
653
|
-
updatedAt: string
|
|
654
|
-
markdown: string
|
|
655
|
-
}>
|
|
656
|
-
errorRoutes: string[]
|
|
657
|
-
}>
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
declare module '#ai-ready-virtual/page-data.mjs' {
|
|
661
|
-
export const pages: never[]
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
declare module '#ai-ready/adapter' {
|
|
665
|
-
import type { Connector } from 'db0'
|
|
666
|
-
const connector: (config: unknown) => Connector
|
|
667
|
-
export default connector
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
export {}
|
|
671
|
-
`
|
|
672
|
-
}, { nitro: true });
|
|
488
|
+
registerTypeTemplates();
|
|
673
489
|
const defaultLlmsTxtSections = [];
|
|
674
490
|
const llmsFullRoute = withSiteUrl("llms-full.txt");
|
|
675
491
|
defaultLlmsTxtSections.push({
|
|
@@ -720,24 +536,55 @@ export {}
|
|
|
720
536
|
await nuxt.callHook("ai-ready:llms-txt", llmsTxtPayload);
|
|
721
537
|
mergedLlmsTxt.sections = llmsTxtPayload.sections;
|
|
722
538
|
mergedLlmsTxt.notes = llmsTxtPayload.notes.length > 0 ? llmsTxtPayload.notes : void 0;
|
|
723
|
-
const prerenderCacheDir = join
|
|
724
|
-
const
|
|
539
|
+
const prerenderCacheDir = join(nuxt.options.rootDir, "node_modules/.cache/nuxt-seo/ai-ready/routes");
|
|
540
|
+
const buildDbPath = join(nuxt.options.buildDir, ".data/ai-ready/build.db");
|
|
725
541
|
nuxt.hooks.hook("nitro:config", (nitroConfig) => {
|
|
726
542
|
nitroConfig.experimental = nitroConfig.experimental || {};
|
|
727
543
|
nitroConfig.experimental.asyncContext = true;
|
|
544
|
+
const runtimeSyncEnabled2 = config.runtimeSync?.enabled ?? false;
|
|
545
|
+
const cron = config.runtimeSync?.cron;
|
|
546
|
+
if (runtimeSyncEnabled2 && cron) {
|
|
547
|
+
nitroConfig.tasks = nitroConfig.tasks || {};
|
|
548
|
+
nitroConfig.tasks["ai-ready:index"] = {
|
|
549
|
+
handler: resolve("./runtime/server/tasks/ai-ready-index")
|
|
550
|
+
};
|
|
551
|
+
nitroConfig.scheduledTasks = nitroConfig.scheduledTasks || {};
|
|
552
|
+
nitroConfig.scheduledTasks[cron] = nitroConfig.scheduledTasks[cron] || [];
|
|
553
|
+
nitroConfig.scheduledTasks[cron].push("ai-ready:index");
|
|
554
|
+
}
|
|
728
555
|
nitroConfig.virtual = nitroConfig.virtual || {};
|
|
729
556
|
nitroConfig.virtual["#ai-ready-virtual/read-page-data.mjs"] = `
|
|
730
|
-
import { readFile } from 'node:fs/promises'
|
|
731
|
-
|
|
732
557
|
export async function readPageDataFromFilesystem() {
|
|
733
558
|
if (!import.meta.prerender) {
|
|
734
559
|
return { pages: [], errorRoutes: [] }
|
|
735
560
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
const
|
|
561
|
+
|
|
562
|
+
const dbPath = ${JSON.stringify(buildDbPath)}
|
|
563
|
+
|
|
564
|
+
// Check if database file exists
|
|
565
|
+
const { existsSync } = await import('node:fs')
|
|
566
|
+
if (!existsSync(dbPath)) {
|
|
567
|
+
return { pages: [], errorRoutes: [] }
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// Use better-sqlite3 to read pages
|
|
571
|
+
const Database = (await import('better-sqlite3')).default
|
|
572
|
+
const db = new Database(dbPath, { readonly: true })
|
|
573
|
+
|
|
574
|
+
const rows = db.prepare('SELECT route, title, description, markdown, headings, keywords, updated_at, is_error FROM ai_ready_pages').all()
|
|
575
|
+
db.close()
|
|
576
|
+
|
|
577
|
+
const pages = rows.filter(r => !r.is_error).map(r => ({
|
|
578
|
+
route: r.route,
|
|
579
|
+
title: r.title,
|
|
580
|
+
description: r.description,
|
|
581
|
+
markdown: r.markdown,
|
|
582
|
+
headings: r.headings,
|
|
583
|
+
keywords: JSON.parse(r.keywords || '[]'),
|
|
584
|
+
updatedAt: r.updated_at,
|
|
585
|
+
}))
|
|
586
|
+
const errorRoutes = rows.filter(r => r.is_error).map(r => r.route)
|
|
587
|
+
|
|
741
588
|
return { pages, errorRoutes }
|
|
742
589
|
}
|
|
743
590
|
`;
|
|
@@ -745,6 +592,9 @@ export async function readPageDataFromFilesystem() {
|
|
|
745
592
|
export const errorRoutes = []`;
|
|
746
593
|
});
|
|
747
594
|
const database = refineDatabaseConfig(config.database || {}, nuxt.options.rootDir);
|
|
595
|
+
const runtimeSyncEnabled = config.runtimeSync?.enabled ?? false;
|
|
596
|
+
const indexNowKey = config.indexNow?.key || process.env.NUXT_AI_READY_INDEXNOW_KEY;
|
|
597
|
+
const indexNowEnabled = !!(config.indexNow?.enabled !== false && indexNowKey);
|
|
748
598
|
nuxt.options.runtimeConfig["nuxt-ai-ready"] = {
|
|
749
599
|
version: version || "0.0.0",
|
|
750
600
|
debug: config.debug || false,
|
|
@@ -756,12 +606,24 @@ export const errorRoutes = []`;
|
|
|
756
606
|
llmsTxt: mergedLlmsTxt,
|
|
757
607
|
cacheMaxAgeSeconds: config.cacheMaxAgeSeconds ?? 600,
|
|
758
608
|
prerenderCacheDir,
|
|
759
|
-
|
|
760
|
-
|
|
609
|
+
database,
|
|
610
|
+
runtimeSync: {
|
|
611
|
+
enabled: runtimeSyncEnabled,
|
|
612
|
+
ttl: config.runtimeSync?.ttl ?? 3600,
|
|
613
|
+
batchSize: config.runtimeSync?.batchSize ?? 20,
|
|
614
|
+
secret: config.runtimeSync?.secret,
|
|
615
|
+
pruneTtl: config.runtimeSync?.pruneTtl ?? 0
|
|
616
|
+
},
|
|
617
|
+
indexNow: indexNowEnabled ? {
|
|
618
|
+
enabled: true,
|
|
619
|
+
key: indexNowKey,
|
|
620
|
+
host: config.indexNow?.host || "api.indexnow.org"
|
|
621
|
+
} : void 0
|
|
761
622
|
};
|
|
762
623
|
nuxt.options.nitro.plugins = nuxt.options.nitro.plugins || [];
|
|
763
624
|
nuxt.options.nitro.plugins.push(resolve("./runtime/server/plugins/db-restore"));
|
|
764
|
-
|
|
625
|
+
if (runtimeSyncEnabled)
|
|
626
|
+
nuxt.options.nitro.plugins.push(resolve("./runtime/server/plugins/sitemap-seeder"));
|
|
765
627
|
addServerHandler({
|
|
766
628
|
middleware: true,
|
|
767
629
|
handler: resolve("./runtime/server/middleware/markdown.prerender")
|
|
@@ -773,7 +635,7 @@ export const errorRoutes = []`;
|
|
|
773
635
|
if (nuxt.options.build) {
|
|
774
636
|
addPlugin({
|
|
775
637
|
mode: "server",
|
|
776
|
-
src: resolve("./runtime/
|
|
638
|
+
src: resolve("./runtime/app/plugins/md-hints.prerender")
|
|
777
639
|
});
|
|
778
640
|
}
|
|
779
641
|
addServerHandler({ route: "/llms.txt", handler: resolve("./runtime/server/routes/llms.txt.get") });
|
|
@@ -781,6 +643,18 @@ export const errorRoutes = []`;
|
|
|
781
643
|
if (config.debug) {
|
|
782
644
|
addServerHandler({ route: "/__ai-ready-debug", handler: resolve("./runtime/server/routes/__ai-ready-debug.get") });
|
|
783
645
|
}
|
|
646
|
+
if (runtimeSyncEnabled) {
|
|
647
|
+
addServerHandler({ route: "/__ai-ready/status", handler: resolve("./runtime/server/routes/__ai-ready/status.get") });
|
|
648
|
+
addServerHandler({ route: "/__ai-ready/poll", method: "post", handler: resolve("./runtime/server/routes/__ai-ready/poll.post") });
|
|
649
|
+
addServerHandler({ route: "/__ai-ready/prune", method: "post", handler: resolve("./runtime/server/routes/__ai-ready/prune.post") });
|
|
650
|
+
}
|
|
651
|
+
if (indexNowEnabled && indexNowKey) {
|
|
652
|
+
addServerHandler({ route: `/${indexNowKey}.txt`, handler: resolve("./runtime/server/routes/indexnow-key.get") });
|
|
653
|
+
addServerHandler({ route: "/__ai-ready/indexnow", method: "post", handler: resolve("./runtime/server/routes/__ai-ready/indexnow.post") });
|
|
654
|
+
if (!runtimeSyncEnabled) {
|
|
655
|
+
addServerHandler({ route: "/__ai-ready/status", handler: resolve("./runtime/server/routes/__ai-ready/status.get") });
|
|
656
|
+
}
|
|
657
|
+
}
|
|
784
658
|
const isStatic = nuxt.options.nitro.static || nuxt.options._generate || false;
|
|
785
659
|
const hasPrerenderedRoutes = nuxt.options.nitro.prerender?.routes?.length;
|
|
786
660
|
const isSPA = nuxt.options.ssr === false;
|
|
@@ -794,7 +668,7 @@ export const errorRoutes = []`;
|
|
|
794
668
|
}
|
|
795
669
|
if (isStatic || hasPrerenderedRoutes) {
|
|
796
670
|
const siteConfig = useSiteConfig();
|
|
797
|
-
setupPrerenderHandler(
|
|
671
|
+
setupPrerenderHandler(buildDbPath, {
|
|
798
672
|
name: siteConfig.name,
|
|
799
673
|
url: siteConfig.url,
|
|
800
674
|
description: siteConfig.description
|
|
@@ -805,7 +679,7 @@ export const errorRoutes = []`;
|
|
|
805
679
|
nuxt.options.nitro.routeRules["/llms-full.txt"] = { headers: { "Content-Type": "text/plain; charset=utf-8" } };
|
|
806
680
|
nuxt.hooks.hook("nitro:build:before", (nitro) => {
|
|
807
681
|
nitro.hooks.hook("compiled", async () => {
|
|
808
|
-
const headersPath = join
|
|
682
|
+
const headersPath = join(nitro.options.output.publicDir, "_headers");
|
|
809
683
|
const exists = await access(headersPath).then(() => true).catch(() => false);
|
|
810
684
|
if (exists) {
|
|
811
685
|
await appendFile(headersPath, `
|