nuxt-ai-ready 2.0.1 → 2.2.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/chunks/agent-skills.mjs +4 -1
- package/dist/chunks/prerender.mjs +10 -3
- package/dist/cli.mjs +67 -11
- package/dist/module.d.mts +3 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +4 -1
- package/dist/runtime/app/plugins/md-alternate.server.js +6 -2
- package/dist/runtime/llms-txt-utils.js +2 -2
- package/dist/runtime/markdown-path.d.ts +2 -0
- package/dist/runtime/markdown-path.js +4 -0
- package/dist/runtime/server/db/context.d.ts +2 -0
- package/dist/runtime/server/db/context.js +2 -0
- package/dist/runtime/server/db/drizzle/client.d.ts +6 -1
- package/dist/runtime/server/db/drizzle/client.js +66 -13
- package/dist/runtime/server/db/drizzle/index.d.ts +2 -2
- package/dist/runtime/server/db/drizzle/index.js +1 -2
- package/dist/runtime/server/db/drizzle/providers/dbPath.js +1 -1
- package/dist/runtime/server/db/drizzle/providers/postgres.d.ts +8 -0
- package/dist/runtime/server/db/drizzle/providers/postgres.js +17 -0
- package/dist/runtime/server/db/drizzle/queries.d.ts +1 -5
- package/dist/runtime/server/db/drizzle/queries.js +79 -38
- package/dist/runtime/server/db/drizzle/raw.d.ts +2 -2
- package/dist/runtime/server/db/drizzle/raw.js +32 -13
- package/dist/runtime/server/db/queries.d.ts +30 -27
- package/dist/runtime/server/db/queries.js +142 -91
- package/dist/runtime/server/db/schema/postgres.d.ts +20 -20
- package/dist/runtime/server/db/schema/postgres.js +6 -6
- package/dist/runtime/server/db/shared.d.ts +16 -0
- package/dist/runtime/server/db/shared.js +12 -0
- package/dist/runtime/server/middleware/markdown.js +10 -5
- package/dist/runtime/server/middleware/markdown.prerender.js +4 -1
- package/dist/runtime/server/plugins/db-lifecycle.js +5 -4
- package/dist/runtime/server/plugins/sitemap-seeder.js +9 -4
- package/dist/runtime/server/routes/__ai-ready/cron.get.js +10 -2
- package/dist/runtime/server/routes/__ai-ready/poll.post.js +30 -14
- package/dist/runtime/server/routes/__ai-ready/reindex.post.d.ts +2 -0
- package/dist/runtime/server/routes/__ai-ready/reindex.post.js +23 -0
- package/dist/runtime/server/routes/api-catalog.js +11 -2
- package/dist/runtime/server/routes/llms.txt.get.js +17 -12
- package/dist/runtime/server/routes/sitemap.md.get.d.ts +2 -0
- package/dist/runtime/server/routes/sitemap.md.get.js +51 -0
- package/dist/runtime/server/tasks/ai-ready-cron.js +3 -0
- package/dist/runtime/server/utils/i18n.d.ts +4 -0
- package/dist/runtime/server/utils/i18n.js +13 -0
- package/dist/runtime/server/utils/indexPage.d.ts +0 -1
- package/dist/runtime/server/utils/indexPage.js +10 -4
- package/dist/runtime/server/utils/link-header.d.ts +2 -0
- package/dist/runtime/server/utils/link-header.js +6 -0
- package/dist/runtime/server/utils/markdown-request.js +3 -1
- package/dist/runtime/server/utils/negotiation-decision.d.ts +1 -1
- package/dist/runtime/server/utils/runCron.d.ts +24 -0
- package/dist/runtime/server/utils/runCron.js +21 -4
- package/dist/runtime/server/utils/sitemap-md.d.ts +13 -0
- package/dist/runtime/server/utils/sitemap-md.js +65 -0
- package/dist/runtime/types.d.ts +21 -7
- package/dist/shared/{nuxt-ai-ready.D8qbnNrv.mjs → nuxt-ai-ready.D5URd0TD.mjs} +107 -39
- package/package.json +31 -26
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { toMarkdownPath } from "../../markdown-path.js";
|
|
2
|
+
import { toDeployedRoute } from "../../route-path.js";
|
|
3
|
+
export const SITEMAP_MD_ROUTE = "/sitemap.md";
|
|
4
|
+
function topSegment(route) {
|
|
5
|
+
return route.split("/").filter(Boolean)[0] || "";
|
|
6
|
+
}
|
|
7
|
+
function escapeTitle(title) {
|
|
8
|
+
return title.replaceAll("[", "\\[").replaceAll("]", "\\]");
|
|
9
|
+
}
|
|
10
|
+
function formatEntry(entry, resolveHref) {
|
|
11
|
+
const title = escapeTitle(entry.title?.trim() || entry.route);
|
|
12
|
+
const href = resolveHref(entry.route);
|
|
13
|
+
if (entry.updatedAt) {
|
|
14
|
+
const parsed = new Date(entry.updatedAt);
|
|
15
|
+
if (!Number.isNaN(parsed.getTime()))
|
|
16
|
+
return `- [${title}](${href} "${parsed.toISOString()}")`;
|
|
17
|
+
}
|
|
18
|
+
return `- [${title}](${href})`;
|
|
19
|
+
}
|
|
20
|
+
export function buildSitemapMd(entries, options = {}) {
|
|
21
|
+
const resolveHref = options.resolveHref || toMarkdownPath;
|
|
22
|
+
const groups = /* @__PURE__ */ new Map();
|
|
23
|
+
for (const entry of [...entries].sort((a, b) => a.route.localeCompare(b.route))) {
|
|
24
|
+
const key = topSegment(entry.route);
|
|
25
|
+
const group = groups.get(key);
|
|
26
|
+
if (group)
|
|
27
|
+
group.push(entry);
|
|
28
|
+
else
|
|
29
|
+
groups.set(key, [entry]);
|
|
30
|
+
}
|
|
31
|
+
const keys = [...groups.keys()].sort((a, b) => {
|
|
32
|
+
if (a === "")
|
|
33
|
+
return -1;
|
|
34
|
+
if (b === "")
|
|
35
|
+
return 1;
|
|
36
|
+
return a.localeCompare(b);
|
|
37
|
+
});
|
|
38
|
+
const parts = [`# ${options.siteName || "Site"} Sitemap`, "", "All pages in Markdown format."];
|
|
39
|
+
for (const key of keys) {
|
|
40
|
+
parts.push("", `## ${key || "Root"}`);
|
|
41
|
+
for (const entry of groups.get(key))
|
|
42
|
+
parts.push(formatEntry(entry, resolveHref));
|
|
43
|
+
}
|
|
44
|
+
return `${parts.join("\n")}
|
|
45
|
+
`;
|
|
46
|
+
}
|
|
47
|
+
export function appendSitemapSection(markdown, sitemapHref = SITEMAP_MD_ROUTE) {
|
|
48
|
+
const section = `## Sitemap
|
|
49
|
+
|
|
50
|
+
See the full [sitemap](${sitemapHref}) for all pages.`;
|
|
51
|
+
const trimmed = markdown.trimEnd();
|
|
52
|
+
if (trimmed.endsWith(section))
|
|
53
|
+
return `${trimmed}
|
|
54
|
+
`;
|
|
55
|
+
return `${trimmed}
|
|
56
|
+
|
|
57
|
+
${section}
|
|
58
|
+
`;
|
|
59
|
+
}
|
|
60
|
+
export function isSitemapMdRequest(path, baseURL, enabled) {
|
|
61
|
+
if (!enabled)
|
|
62
|
+
return false;
|
|
63
|
+
const withoutQuery = path.split("?")[0] || path;
|
|
64
|
+
return withoutQuery === SITEMAP_MD_ROUTE || withoutQuery === toDeployedRoute(SITEMAP_MD_ROUTE, baseURL);
|
|
65
|
+
}
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -149,6 +149,19 @@ export interface ModuleOptions {
|
|
|
149
149
|
* Structured llms.txt configuration
|
|
150
150
|
*/
|
|
151
151
|
llmsTxt?: LlmsTxtConfig;
|
|
152
|
+
/**
|
|
153
|
+
* Generate a Markdown sitemap at `/sitemap.md` from indexed pages and end
|
|
154
|
+
* every Markdown page with a `## Sitemap` section linking to it.
|
|
155
|
+
* @default true
|
|
156
|
+
*/
|
|
157
|
+
sitemapMd?: boolean;
|
|
158
|
+
/**
|
|
159
|
+
* Advertise llms.txt as `rel="describedby"` from HTML and Markdown
|
|
160
|
+
* responses, as the llms.txt v2 spec requires. Emits an HTML `<link>` tag
|
|
161
|
+
* and a Link header entry pointing at the llms.txt route.
|
|
162
|
+
* @default true
|
|
163
|
+
*/
|
|
164
|
+
describedby?: boolean;
|
|
152
165
|
/**
|
|
153
166
|
* Content Signal Directives
|
|
154
167
|
*/
|
|
@@ -231,7 +244,7 @@ export interface ModuleOptions {
|
|
|
231
244
|
llmsTxtCacheSeconds?: number;
|
|
232
245
|
/**
|
|
233
246
|
* Database configuration for page storage
|
|
234
|
-
* Supports SQLite, LibSQL/Turso, Cloudflare D1, and
|
|
247
|
+
* Supports SQLite, LibSQL/Turso, Cloudflare D1, Neon, and PostgreSQL
|
|
235
248
|
*
|
|
236
249
|
* Storage stays off until a runtime feature or page tool needs it.
|
|
237
250
|
* Set to `false` (or `{ type: 'none' }`) to force it off.
|
|
@@ -247,9 +260,10 @@ export interface ModuleOptions {
|
|
|
247
260
|
* - 'bun': Bun SQLite via bun:sqlite (auto-detected on Bun) [experimental]
|
|
248
261
|
* - 'libsql': Turso/LibSQL [experimental]
|
|
249
262
|
* - 'neon': Vercel Postgres via Neon serverless (auto-detected on Vercel with POSTGRES_URL) [experimental]
|
|
263
|
+
* - 'postgres': PostgreSQL via Postgres.js [experimental]
|
|
250
264
|
* @default 'sqlite' when a requested feature needs storage
|
|
251
265
|
*/
|
|
252
|
-
type?: 'none' | 'sqlite' | 'bun' | 'd1' | 'libsql' | 'neon';
|
|
266
|
+
type?: 'none' | 'sqlite' | 'bun' | 'd1' | 'libsql' | 'neon' | 'postgres';
|
|
253
267
|
/**
|
|
254
268
|
* SQLite filename (relative to rootDir or absolute)
|
|
255
269
|
* @default '.data/ai-ready/pages.db'
|
|
@@ -257,12 +271,12 @@ export interface ModuleOptions {
|
|
|
257
271
|
filename?: string;
|
|
258
272
|
/**
|
|
259
273
|
* D1 binding name for Cloudflare Workers/Pages
|
|
260
|
-
* @default '
|
|
274
|
+
* @default 'DB'
|
|
261
275
|
*/
|
|
262
276
|
bindingName?: string;
|
|
263
277
|
/**
|
|
264
|
-
* Database URL for LibSQL/Turso or
|
|
265
|
-
*
|
|
278
|
+
* Database URL for LibSQL/Turso, Neon, or PostgreSQL
|
|
279
|
+
* PostgreSQL drivers also read POSTGRES_URL or DATABASE_URL.
|
|
266
280
|
*/
|
|
267
281
|
url?: string;
|
|
268
282
|
/**
|
|
@@ -271,7 +285,7 @@ export interface ModuleOptions {
|
|
|
271
285
|
authToken?: string;
|
|
272
286
|
};
|
|
273
287
|
/**
|
|
274
|
-
* Enable scheduled cron task (runs every
|
|
288
|
+
* Enable scheduled cron task (runs every 5 minutes)
|
|
275
289
|
* When true, automatically enables runtimeSync for background indexing
|
|
276
290
|
*/
|
|
277
291
|
cron?: boolean;
|
|
@@ -321,7 +335,7 @@ export interface ModuleOptions {
|
|
|
321
335
|
ttl?: number;
|
|
322
336
|
/**
|
|
323
337
|
* Pages to index per batch
|
|
324
|
-
* @default
|
|
338
|
+
* @default 50
|
|
325
339
|
*/
|
|
326
340
|
batchSize?: number;
|
|
327
341
|
/**
|
|
@@ -8,9 +8,12 @@ import { installNuxtSiteConfig, useSiteConfig, withSiteUrl } from 'nuxt-site-con
|
|
|
8
8
|
import { resolveNuxtContentVersion, renderNitroTypeAugmentations, setupNitroRuntimeCompatibility } from 'nuxtseo-shared/kit';
|
|
9
9
|
import { readPackageJSON, resolvePackageJSON } from 'pkg-types';
|
|
10
10
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
11
|
+
import { isReservedPath, normalizePagePath, toMarkdownPath } from '../../dist/runtime/markdown-path.js';
|
|
12
|
+
import { SITEMAP_MD_ROUTE } from '../../dist/runtime/server/utils/sitemap-md.js';
|
|
11
13
|
import { MCP_SERVER_CARD_MEDIA_TYPE, AI_CATALOG_MEDIA_TYPE } from '../../dist/runtime/server/utils/discovery-response.js';
|
|
12
14
|
import { isAbsolute, join as join$1 } from 'pathe';
|
|
13
15
|
import { resolveI18nConfig, toRuntimeI18nConfig as toRuntimeI18nConfig$1 } from 'nuxtseo-shared/i18n';
|
|
16
|
+
import { toDeployedRoute } from '../../dist/runtime/route-path.js';
|
|
14
17
|
|
|
15
18
|
const SELECT_PAGE_DATA = "SELECT route, title, description, markdown, headings, keywords, updated_at, is_error, locale FROM ai_ready_pages";
|
|
16
19
|
function createBuildPageDataVirtual(options) {
|
|
@@ -175,7 +178,7 @@ function resolveModuleEntryUrl(url) {
|
|
|
175
178
|
|
|
176
179
|
const MARKDOWN_LINK_AVAILABILITY_FILE = "markdown-link-availability.json";
|
|
177
180
|
|
|
178
|
-
const
|
|
181
|
+
const webmcpTypes = `
|
|
179
182
|
import type { WebMcpToolsContext } from 'nuxt-ai-ready/webmcp'
|
|
180
183
|
|
|
181
184
|
declare module '#app' {
|
|
@@ -184,37 +187,6 @@ declare module '#app' {
|
|
|
184
187
|
'ai-ready:webmcp:tools': (context: WebMcpToolsContext) => void | Promise<void>
|
|
185
188
|
}
|
|
186
189
|
}
|
|
187
|
-
|
|
188
|
-
declare module '@vue/runtime-dom' {
|
|
189
|
-
interface HTMLAttributes {
|
|
190
|
-
/** Names the tool this form exposes to agents. */
|
|
191
|
-
toolname?: string
|
|
192
|
-
/** Describes what submitting this form does. */
|
|
193
|
-
tooldescription?: string
|
|
194
|
-
/** Describes this field as a tool parameter. */
|
|
195
|
-
toolparamdescription?: string
|
|
196
|
-
/** Submit the form as soon as an agent fills it in. */
|
|
197
|
-
toolautosubmit?: boolean | ''
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
declare global {
|
|
202
|
-
interface SubmitEvent {
|
|
203
|
-
/** Whether an agent triggered this submit through a tool call. */
|
|
204
|
-
readonly agentInvoked?: boolean
|
|
205
|
-
/** Return a result to the agent. Call preventDefault() first. */
|
|
206
|
-
respondWith?: (result: Promise<unknown>) => void
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
interface WebMcpToolEvent extends Event {
|
|
210
|
-
readonly toolName: string
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
interface WindowEventMap {
|
|
214
|
-
toolactivated: WebMcpToolEvent
|
|
215
|
-
toolcancel: WebMcpToolEvent
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
190
|
`;
|
|
219
191
|
function registerTypeTemplates(ctx) {
|
|
220
192
|
const nitroTypes = renderNitroTypeAugmentations(ctx.nitroCompatibility, {
|
|
@@ -231,7 +203,7 @@ import type { MarkdownContext, MarkdownSourceContext, PageIndexedContext } from
|
|
|
231
203
|
import type { MdreamOptions } from 'mdream'
|
|
232
204
|
|
|
233
205
|
${nitroTypes}
|
|
234
|
-
${ctx.config.webmcp ?
|
|
206
|
+
${ctx.config.webmcp ? webmcpTypes : ""}
|
|
235
207
|
export {}
|
|
236
208
|
`
|
|
237
209
|
});
|
|
@@ -598,7 +570,7 @@ function resolveDatabaseConfig(input) {
|
|
|
598
570
|
database: { _tag: "Enabled", type, bindingName: config.bindingName || "DB" }
|
|
599
571
|
};
|
|
600
572
|
}
|
|
601
|
-
if (type === "neon") {
|
|
573
|
+
if (type === "neon" || type === "postgres") {
|
|
602
574
|
return {
|
|
603
575
|
_tag: "Resolved",
|
|
604
576
|
logs,
|
|
@@ -892,6 +864,57 @@ function ensureStaticHeader(contents, route, name, value) {
|
|
|
892
864
|
return `${contents.slice(0, blockStart)}${prefix} ${name}: ${value}${eol}${contents.slice(blockStart)}`;
|
|
893
865
|
}
|
|
894
866
|
|
|
867
|
+
const RE_MD_EXT = /\.md$/;
|
|
868
|
+
function isStaticMarkdownSourceRoute(route) {
|
|
869
|
+
if (route.includes("*") || route.includes(":"))
|
|
870
|
+
return false;
|
|
871
|
+
const path = route.split("?")[0] || route;
|
|
872
|
+
if (isReservedPath(path))
|
|
873
|
+
return false;
|
|
874
|
+
const lastSegment = path.split("/").pop() || "";
|
|
875
|
+
return !lastSegment.includes(".");
|
|
876
|
+
}
|
|
877
|
+
function staticDescribedbyEntry(baseURL) {
|
|
878
|
+
return `<${encodeURI(toDeployedRoute("/llms.txt", baseURL))}>; rel="describedby"`;
|
|
879
|
+
}
|
|
880
|
+
function buildStaticMarkdownLinkHeader(route, baseURL, describedby) {
|
|
881
|
+
const htmlRoute = encodeURI(toDeployedRoute(normalizePagePath(route), baseURL));
|
|
882
|
+
const parts = [
|
|
883
|
+
`<${htmlRoute}>; rel="alternate"; type="text/html"`,
|
|
884
|
+
`<${htmlRoute}>; rel="canonical"`
|
|
885
|
+
];
|
|
886
|
+
if (describedby)
|
|
887
|
+
parts.push(staticDescribedbyEntry(baseURL));
|
|
888
|
+
return parts.join(", ");
|
|
889
|
+
}
|
|
890
|
+
function pageRouteFromMarkdownTwin(fileName) {
|
|
891
|
+
if (!fileName?.endsWith(".md") || fileName === SITEMAP_MD_ROUTE)
|
|
892
|
+
return null;
|
|
893
|
+
const pageRoute = normalizePagePath(fileName.replace(RE_MD_EXT, ""));
|
|
894
|
+
if (pageRoute === "/index")
|
|
895
|
+
return "/";
|
|
896
|
+
return isStaticMarkdownSourceRoute(pageRoute) ? pageRoute : null;
|
|
897
|
+
}
|
|
898
|
+
function prerenderedMarkdownHeaderRules(prerenderedRoutes, baseURL, describedby) {
|
|
899
|
+
const rules = /* @__PURE__ */ new Map();
|
|
900
|
+
for (const entry of prerenderedRoutes) {
|
|
901
|
+
const pageRoute = pageRouteFromMarkdownTwin(entry.fileName);
|
|
902
|
+
if (pageRoute === null)
|
|
903
|
+
continue;
|
|
904
|
+
const mdRoute = toMarkdownPath(pageRoute);
|
|
905
|
+
if (!rules.has(mdRoute)) {
|
|
906
|
+
rules.set(mdRoute, {
|
|
907
|
+
route: mdRoute,
|
|
908
|
+
headers: {
|
|
909
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
910
|
+
"Link": buildStaticMarkdownLinkHeader(pageRoute, baseURL, describedby)
|
|
911
|
+
}
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
return [...rules.values()];
|
|
916
|
+
}
|
|
917
|
+
|
|
895
918
|
const DEFAULT_MAX_OUTPUT_CHARS = 1500;
|
|
896
919
|
const DEFAULT_LIST_LIMIT = 20;
|
|
897
920
|
const DEFAULT_SEARCH_LIMIT = 10;
|
|
@@ -1216,7 +1239,12 @@ ${details}`);
|
|
|
1216
1239
|
userAgent: "*",
|
|
1217
1240
|
// Preserve nuxt-robots' default wildcard rule so the injected group remains valid.
|
|
1218
1241
|
disallow: [""],
|
|
1219
|
-
|
|
1242
|
+
// Object form renders one comma-separated line; an array renders one line per entry, which validators reject.
|
|
1243
|
+
contentSignal: {
|
|
1244
|
+
"ai-train": config.contentSignal.aiTrain ? "yes" : "no",
|
|
1245
|
+
"search": config.contentSignal.search ? "yes" : "no",
|
|
1246
|
+
"ai-input": config.contentSignal.aiInput ? "yes" : "no"
|
|
1247
|
+
}
|
|
1220
1248
|
};
|
|
1221
1249
|
if (config.contentSignal.contentUsage !== false)
|
|
1222
1250
|
group.contentUsage = [`train-ai=${config.contentSignal.aiTrain ? "y" : "n"}`];
|
|
@@ -1579,12 +1607,13 @@ export const logger = createModuleLogger('nuxt-ai-ready', ${!!config.debug})
|
|
|
1579
1607
|
bun: "#ai-ready/server/db/drizzle/providers/bun",
|
|
1580
1608
|
d1: "#ai-ready/server/db/drizzle/providers/d1",
|
|
1581
1609
|
libsql: "#ai-ready/server/db/drizzle/providers/libsql",
|
|
1582
|
-
neon: "#ai-ready/server/db/drizzle/providers/neon"
|
|
1610
|
+
neon: "#ai-ready/server/db/drizzle/providers/neon",
|
|
1611
|
+
postgres: "#ai-ready/server/db/drizzle/providers/postgres"
|
|
1583
1612
|
};
|
|
1584
1613
|
nitroConfig.virtual["#ai-ready-virtual/db-provider.mjs"] = database._tag === "Enabled" ? `export { createClient } from '${providerMap[database.type] || providerMap.sqlite}'` : `export function createClient() {
|
|
1585
1614
|
throw new Error('[nuxt-ai-ready] The database is disabled. Set \`aiReady.database\` to store pages at runtime.')
|
|
1586
1615
|
}`;
|
|
1587
|
-
const schemaPath = database._tag === "Enabled" && database.type === "neon" ? "#ai-ready/server/db/schema/postgres" : "#ai-ready/server/db/schema/sqlite";
|
|
1616
|
+
const schemaPath = database._tag === "Enabled" && (database.type === "neon" || database.type === "postgres") ? "#ai-ready/server/db/schema/postgres" : "#ai-ready/server/db/schema/sqlite";
|
|
1588
1617
|
nitroConfig.virtual["#ai-ready-virtual/db-schema.mjs"] = `export * from '${schemaPath}'`;
|
|
1589
1618
|
nitroConfig.virtual["#ai-ready-virtual/devtools-meta.mjs"] = `export const devtoolsMeta = ${JSON.stringify({
|
|
1590
1619
|
contentSignal: config.contentSignal || false,
|
|
@@ -1599,6 +1628,8 @@ export const logger = createModuleLogger('nuxt-ai-ready', ${!!config.debug})
|
|
|
1599
1628
|
debugCron: config.debugCron || false,
|
|
1600
1629
|
contentNegotiation: config.contentNegotiation === void 0 ? "auto" : config.contentNegotiation ? "enabled" : "disabled",
|
|
1601
1630
|
mdreamOptions: config.mdreamOptions || {},
|
|
1631
|
+
sitemapMd: config.sitemapMd !== false,
|
|
1632
|
+
describedby: config.describedby !== false,
|
|
1602
1633
|
markdownCacheHeaders: defu(config.markdownCacheHeaders, {
|
|
1603
1634
|
maxAge: 3600,
|
|
1604
1635
|
swr: true
|
|
@@ -1642,6 +1673,9 @@ export const logger = createModuleLogger('nuxt-ai-ready', ${!!config.debug})
|
|
|
1642
1673
|
});
|
|
1643
1674
|
addServerHandler({ route: "/llms.txt", handler: resolve("./runtime/server/routes/llms.txt.get"), lazy: true });
|
|
1644
1675
|
addServerHandler({ route: "/llms-full.txt", handler: resolve("./runtime/server/routes/llms-full.txt.get"), lazy: true });
|
|
1676
|
+
if (config.sitemapMd !== false) {
|
|
1677
|
+
addServerHandler({ route: SITEMAP_MD_ROUTE, handler: resolve("./runtime/server/routes/sitemap.md.get"), lazy: true });
|
|
1678
|
+
}
|
|
1645
1679
|
if (agentSkillsResult._tag === "Enabled") {
|
|
1646
1680
|
addServerHandler({ route: AGENT_SKILLS_INDEX_ROUTE, handler: resolve("./runtime/server/routes/agent-skills-index"), lazy: true });
|
|
1647
1681
|
for (const route of Object.keys(agentSkillsResult.localArtifacts)) {
|
|
@@ -1688,7 +1722,9 @@ export const logger = createModuleLogger('nuxt-ai-ready', ${!!config.debug})
|
|
|
1688
1722
|
addServerHandler({ route: "/__ai-ready/pages", handler: resolve("./runtime/server/routes/__ai-ready/pages.get"), lazy: true });
|
|
1689
1723
|
}
|
|
1690
1724
|
}
|
|
1691
|
-
|
|
1725
|
+
if (nuxt.options.dev || config.debug) {
|
|
1726
|
+
addServerHandler({ route: "/__ai-ready__/debug.json", handler: resolve("./runtime/server/routes/__ai-ready/devtools.get"), lazy: true });
|
|
1727
|
+
}
|
|
1692
1728
|
if (config.debug) {
|
|
1693
1729
|
addServerHandler({ route: "/__ai-ready-debug", handler: resolve("./runtime/server/routes/__ai-ready-debug.get"), lazy: true });
|
|
1694
1730
|
}
|
|
@@ -1697,6 +1733,7 @@ export const logger = createModuleLogger('nuxt-ai-ready', ${!!config.debug})
|
|
|
1697
1733
|
addServerHandler({ route: "/__ai-ready/poll", method: "post", handler: resolve("./runtime/server/routes/__ai-ready/poll.post"), lazy: true });
|
|
1698
1734
|
addServerHandler({ route: "/__ai-ready/prune", method: "post", handler: resolve("./runtime/server/routes/__ai-ready/prune.post"), lazy: true });
|
|
1699
1735
|
addServerHandler({ route: "/__ai-ready/restore", method: "post", handler: resolve("./runtime/server/routes/__ai-ready/restore.post"), lazy: true });
|
|
1736
|
+
addServerHandler({ route: "/__ai-ready/reindex", method: "post", handler: resolve("./runtime/server/routes/__ai-ready/reindex.post"), lazy: true });
|
|
1700
1737
|
addServerPlugin(resolve("./runtime/server/plugins/sitemap-seeder"));
|
|
1701
1738
|
}
|
|
1702
1739
|
if (config.cron && !nuxt.options.dev) {
|
|
@@ -1734,19 +1771,50 @@ export const logger = createModuleLogger('nuxt-ai-ready', ${!!config.debug})
|
|
|
1734
1771
|
for (const route of ["/llms.txt", "/llms-full.txt"]) {
|
|
1735
1772
|
extendRouteRules(route, { headers: { "Content-Type": "text/plain; charset=utf-8" } });
|
|
1736
1773
|
}
|
|
1774
|
+
if (config.sitemapMd !== false) {
|
|
1775
|
+
extendRouteRules(SITEMAP_MD_ROUTE, { headers: { "Content-Type": "text/markdown; charset=utf-8" } });
|
|
1776
|
+
}
|
|
1777
|
+
const staticBaseURL = nuxt.options.app.baseURL || "/";
|
|
1778
|
+
for (const route of nuxt.options.nitro.prerender?.routes || []) {
|
|
1779
|
+
if (!isStaticMarkdownSourceRoute(route))
|
|
1780
|
+
continue;
|
|
1781
|
+
extendRouteRules(toMarkdownPath(route), {
|
|
1782
|
+
headers: {
|
|
1783
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
1784
|
+
"Link": buildStaticMarkdownLinkHeader(route, staticBaseURL, config.describedby !== false)
|
|
1785
|
+
}
|
|
1786
|
+
});
|
|
1787
|
+
}
|
|
1737
1788
|
nuxt.hooks.hook("nitro:build:before", (nitro) => {
|
|
1789
|
+
nitro.hooks.hook("prerender:done", () => {
|
|
1790
|
+
for (const { route, headers } of prerenderedMarkdownHeaderRules(
|
|
1791
|
+
nitro._prerenderedRoutes || [],
|
|
1792
|
+
staticBaseURL,
|
|
1793
|
+
config.describedby !== false
|
|
1794
|
+
)) {
|
|
1795
|
+
nitro.options.routeRules[route] = defu({ headers }, nitro.options.routeRules[route]);
|
|
1796
|
+
}
|
|
1797
|
+
});
|
|
1738
1798
|
nitro.hooks.hook("compiled", async () => {
|
|
1739
1799
|
const headersPath = join(nitro.options.output.publicDir, "_headers");
|
|
1740
1800
|
logger.debug(`Checking for _headers file: ${headersPath}`);
|
|
1741
1801
|
const exists = await access(headersPath).then(() => true).catch(() => false);
|
|
1742
1802
|
if (exists) {
|
|
1743
1803
|
const headers = await readFile(headersPath, "utf8");
|
|
1744
|
-
|
|
1804
|
+
let mergedHeaders = ensureStaticHeader(
|
|
1745
1805
|
headers,
|
|
1746
1806
|
"/*.md",
|
|
1747
1807
|
"Content-Type",
|
|
1748
1808
|
"text/markdown; charset=utf-8"
|
|
1749
1809
|
);
|
|
1810
|
+
if (config.describedby !== false) {
|
|
1811
|
+
mergedHeaders = ensureStaticHeader(
|
|
1812
|
+
mergedHeaders,
|
|
1813
|
+
"/*.md",
|
|
1814
|
+
"Link",
|
|
1815
|
+
staticDescribedbyEntry(nitro.options.baseURL || "/")
|
|
1816
|
+
);
|
|
1817
|
+
}
|
|
1750
1818
|
if (mergedHeaders !== headers) {
|
|
1751
1819
|
await writeFile(headersPath, mergedHeaders);
|
|
1752
1820
|
logger.debug("Merged .md charset header into _headers");
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-ai-ready",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.0
|
|
4
|
+
"version": "2.2.0",
|
|
5
5
|
"description": "Best practice AI & LLM discoverability for Nuxt sites.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -48,7 +48,8 @@
|
|
|
48
48
|
"@libsql/client": "^0.14.0",
|
|
49
49
|
"@neondatabase/serverless": "^1.0.0",
|
|
50
50
|
"@nuxtjs/sitemap": ">=8.3.1",
|
|
51
|
-
"better-sqlite3": "^11.0.0 || ^12.0.0 || ^13.0.0"
|
|
51
|
+
"better-sqlite3": "^11.0.0 || ^12.0.0 || ^13.0.0",
|
|
52
|
+
"postgres": "^3.4.9"
|
|
52
53
|
},
|
|
53
54
|
"peerDependenciesMeta": {
|
|
54
55
|
"@libsql/client": {
|
|
@@ -59,20 +60,23 @@
|
|
|
59
60
|
},
|
|
60
61
|
"better-sqlite3": {
|
|
61
62
|
"optional": true
|
|
63
|
+
},
|
|
64
|
+
"postgres": {
|
|
65
|
+
"optional": true
|
|
62
66
|
}
|
|
63
67
|
},
|
|
64
68
|
"dependencies": {
|
|
65
|
-
"@mdream/js": "^1.7.
|
|
69
|
+
"@mdream/js": "^1.7.1",
|
|
66
70
|
"@nuxt/kit": "^4.5.2",
|
|
67
71
|
"citty": "^0.2.2",
|
|
68
72
|
"consola": "^3.4.2",
|
|
69
73
|
"defu": "^6.1.7",
|
|
70
74
|
"drizzle-orm": "1.0.0-rc.4",
|
|
71
|
-
"mdream": "^1.7.
|
|
75
|
+
"mdream": "^1.7.1",
|
|
72
76
|
"nuxt-site-config": "^4.2.3",
|
|
73
77
|
"nuxtseo-shared": "^5.3.14",
|
|
74
78
|
"pathe": "^2.0.3",
|
|
75
|
-
"pkg-types": "^2.3.
|
|
79
|
+
"pkg-types": "^2.3.2",
|
|
76
80
|
"site-config-stack": "^4.2.3",
|
|
77
81
|
"sitemapd": "^0.2.2",
|
|
78
82
|
"ufo": "^1.6.4",
|
|
@@ -80,43 +84,44 @@
|
|
|
80
84
|
"yaml": "^2.9.0"
|
|
81
85
|
},
|
|
82
86
|
"devDependencies": {
|
|
83
|
-
"@antfu/eslint-config": "^9.
|
|
87
|
+
"@antfu/eslint-config": "^9.5.1",
|
|
84
88
|
"@arethetypeswrong/cli": "^0.18.5",
|
|
85
|
-
"@harlan-zw/comark-content": "0.1.
|
|
86
|
-
"@libsql/client": "^0.
|
|
87
|
-
"@nuxt/content": "^3.
|
|
89
|
+
"@harlan-zw/comark-content": "^0.1.5",
|
|
90
|
+
"@libsql/client": "^0.18.0",
|
|
91
|
+
"@nuxt/content": "^3.16.0",
|
|
88
92
|
"@nuxt/module-builder": "^1.0.3",
|
|
89
|
-
"@nuxt/test-utils": "^4.
|
|
93
|
+
"@nuxt/test-utils": "^4.2.0",
|
|
90
94
|
"@nuxtjs/eslint-config-typescript": "^12.1.0",
|
|
91
95
|
"@nuxtjs/mcp-toolkit": "^0.19.0",
|
|
92
|
-
"@nuxtjs/robots": "^6.
|
|
93
|
-
"@nuxtjs/sitemap": "^8.
|
|
96
|
+
"@nuxtjs/robots": "^6.2.0",
|
|
97
|
+
"@nuxtjs/sitemap": "^8.5.0",
|
|
94
98
|
"@types/better-sqlite3": "^9.6.0",
|
|
95
|
-
"@vitest/coverage-v8": "^
|
|
96
|
-
"@vue/test-utils": "^2.
|
|
99
|
+
"@vitest/coverage-v8": "^5.0.0",
|
|
100
|
+
"@vue/test-utils": "^2.5.0",
|
|
97
101
|
"@vueuse/nuxt": "^14.4.0",
|
|
98
102
|
"better-sqlite3": "^13.0.3",
|
|
99
|
-
"bumpp": "^12.
|
|
100
|
-
"eslint": "^10.
|
|
101
|
-
"eslint-plugin-harlanzw": "^0.
|
|
103
|
+
"bumpp": "^12.3.0",
|
|
104
|
+
"eslint": "^10.10.0",
|
|
105
|
+
"eslint-plugin-harlanzw": "^0.21.1",
|
|
102
106
|
"execa": "^10.0.1",
|
|
103
107
|
"h3": "^1.15.11",
|
|
104
|
-
"happy-dom": "^20.
|
|
108
|
+
"happy-dom": "^20.14.0",
|
|
105
109
|
"nitropack": "^2.13.4",
|
|
106
110
|
"nuxt": "^4.5.2",
|
|
107
111
|
"nuxt-site-config": "^4.2.3",
|
|
108
112
|
"nuxtseo-layer-devtools": "^5.3.14",
|
|
109
|
-
"playwright": "^1.
|
|
110
|
-
"playwright-core": "^1.
|
|
113
|
+
"playwright": "^1.63.0",
|
|
114
|
+
"playwright-core": "^1.63.0",
|
|
115
|
+
"postgres": "^3.4.9",
|
|
111
116
|
"tinyglobby": "^0.2.17",
|
|
112
117
|
"typescript": "6.0.3",
|
|
113
118
|
"unbuild": "^3.6.1",
|
|
114
|
-
"vitest": "^
|
|
115
|
-
"vue": "^3.5.
|
|
116
|
-
"vue-router": "^5.
|
|
117
|
-
"vue-tsc": "^3.3.
|
|
118
|
-
"wrangler": "^4.
|
|
119
|
-
"zod": "^4.4
|
|
119
|
+
"vitest": "^5.0.0",
|
|
120
|
+
"vue": "^3.5.42",
|
|
121
|
+
"vue-router": "^5.3.1",
|
|
122
|
+
"vue-tsc": "^3.3.11",
|
|
123
|
+
"wrangler": "^4.129.0",
|
|
124
|
+
"zod": "^4.5.4"
|
|
120
125
|
},
|
|
121
126
|
"scripts": {
|
|
122
127
|
"lint": "eslint .",
|