nuxt-ai-ready 1.7.11 → 1.7.13
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 +1 -1
- package/dist/chunks/prerender.mjs +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -1
- package/dist/runtime/i18n-url.d.ts +3 -0
- package/dist/runtime/i18n-url.js +8 -0
- package/dist/runtime/llms-txt-format.d.ts +0 -5
- package/dist/runtime/llms-txt-format.js +0 -21
- package/dist/runtime/llms-txt-i18n.d.ts +3 -0
- package/dist/runtime/llms-txt-i18n.js +25 -0
- package/dist/runtime/llms-txt-utils.js +12 -12
- package/dist/runtime/server/middleware/markdown.js +23 -5
- package/dist/runtime/server/plugins/link-header.d.ts +4 -0
- package/dist/runtime/server/plugins/link-header.js +40 -0
- package/dist/runtime/server/plugins/sitemap-seeder.js +1 -1
- package/dist/runtime/server/utils/i18n.d.ts +2 -4
- package/dist/runtime/server/utils/i18n.js +1 -9
- package/dist/runtime/server/utils/link-header.js +2 -1
- package/dist/runtime/server/utils/runCron.js +2 -1
- package/dist/runtime/server/utils/sitemap-routes.d.ts +8 -0
- package/dist/runtime/server/utils/sitemap-routes.js +9 -0
- package/dist/runtime/server/utils/sitemap.d.ts +0 -8
- package/dist/runtime/server/utils/sitemap.js +0 -9
- package/dist/shared/{nuxt-ai-ready.jeGqwuW1.mjs → nuxt-ai-ready.DQXoppXw.mjs} +25 -0
- package/package.json +11 -11
|
@@ -2,7 +2,7 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { realpath, readFile } from 'node:fs/promises';
|
|
3
3
|
import { resolve, isAbsolute, relative, sep } from 'node:path';
|
|
4
4
|
import { parseDocument } from 'yaml';
|
|
5
|
-
import { A as AGENT_SKILLS_SCHEMA } from '../shared/nuxt-ai-ready.
|
|
5
|
+
import { A as AGENT_SKILLS_SCHEMA } from '../shared/nuxt-ai-ready.DQXoppXw.mjs';
|
|
6
6
|
import 'node:module';
|
|
7
7
|
import '@nuxt/kit';
|
|
8
8
|
import 'defu';
|
|
@@ -5,7 +5,7 @@ import { colorize } from 'consola/utils';
|
|
|
5
5
|
import { resolveLocaleFromRoute } from 'nuxtseo-shared/i18n-runtime';
|
|
6
6
|
import { collectSitemap } from 'sitemapd/parse';
|
|
7
7
|
import { withLeadingSlash, withBase, joinURL } from 'ufo';
|
|
8
|
-
import { l as logger, M as MARKDOWN_LINK_AVAILABILITY_FILE } from '../shared/nuxt-ai-ready.
|
|
8
|
+
import { l as logger, M as MARKDOWN_LINK_AVAILABILITY_FILE } from '../shared/nuxt-ai-ready.DQXoppXw.mjs';
|
|
9
9
|
import { normalizePagePath, toMarkdownPath } from '../../dist/runtime/markdown-path.js';
|
|
10
10
|
import { toLogicalRoute, toDeployedRoute } from '../../dist/runtime/route-path.js';
|
|
11
11
|
import { initSchema, computeContentHash, insertPage, queryAllPages, exportDbDump } from '../../dist/runtime/server/db/shared.js';
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import 'defu';
|
|
|
7
7
|
import 'nuxt-site-config/kit';
|
|
8
8
|
import 'nuxtseo-shared/kit';
|
|
9
9
|
import 'pkg-types';
|
|
10
|
-
export { m as default } from './shared/nuxt-ai-ready.
|
|
10
|
+
export { m as default } from './shared/nuxt-ai-ready.DQXoppXw.mjs';
|
|
11
11
|
import '../dist/runtime/server/utils/discovery-response.js';
|
|
12
12
|
import 'node:url';
|
|
13
13
|
import 'pathe';
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { LocaleAlternate } from 'nuxtseo-shared/i18n-runtime';
|
|
2
|
+
/** Resolve a locale alternate against its configured domain or the current site. */
|
|
3
|
+
export declare function resolveLocaleAlternateUrl(alternate: Pick<LocaleAlternate, 'domain' | 'path'>, resolveUrl: (path: string) => string): string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function resolveLocaleAlternateUrl(alternate, resolveUrl) {
|
|
2
|
+
const resolved = resolveUrl(alternate.path);
|
|
3
|
+
if (!alternate.domain)
|
|
4
|
+
return resolved;
|
|
5
|
+
const origin = /^[a-z][a-z\d+.-]*:\/\//i.test(alternate.domain) ? alternate.domain : `https://${alternate.domain}`;
|
|
6
|
+
const deployed = new URL(resolved, "http://nuxtseo.local");
|
|
7
|
+
return new URL(`${deployed.pathname}${deployed.search}${deployed.hash}`, origin).href;
|
|
8
|
+
}
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure formatting functions for llms.txt - no runtime dependencies
|
|
3
|
-
*/
|
|
4
|
-
import type { RuntimeI18nConfig, RuntimeRouteContext } from './server/utils/i18n.js';
|
|
5
1
|
import type { LlmsTxtConfig } from './types.js';
|
|
6
|
-
export declare function formatAvailableLanguagesSection(i18n: RuntimeI18nConfig, pageCounts: Map<string, number>, resolveHref?: (pathname: string) => string, routeContext?: RuntimeRouteContext): string[];
|
|
7
2
|
interface LlmsTxtPageLink {
|
|
8
3
|
pathname: string;
|
|
9
4
|
href?: string;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { computeLocaleAlternates, localePath, resolveLocaleAlternateUrl } from "./server/utils/i18n.js";
|
|
2
1
|
const RE_INLINE_WHITESPACE = /\s+/g;
|
|
3
2
|
const RE_LINK_TITLE_BRACKET = /[[\]]/g;
|
|
4
3
|
const RE_LINK_HREF_UNSAFE = /[\s()]/g;
|
|
@@ -62,26 +61,6 @@ function normalizeOptionalSections(sections) {
|
|
|
62
61
|
function normalizeRequiredSections(sections) {
|
|
63
62
|
return sections.filter((section) => section.links?.length).map(normalizeSection);
|
|
64
63
|
}
|
|
65
|
-
export function formatAvailableLanguagesSection(i18n, pageCounts, resolveHref = (pathname) => pathname, routeContext = {}) {
|
|
66
|
-
const lines = ["## Available Languages on Website", ""];
|
|
67
|
-
const rootAlternates = new Map(computeLocaleAlternates("/", i18n, routeContext).map((alternate) => [alternate.code, alternate]));
|
|
68
|
-
for (const locale of i18n.locales) {
|
|
69
|
-
const isDefault = locale.code === i18n.defaultLocale;
|
|
70
|
-
const alternate = rootAlternates.get(locale.code);
|
|
71
|
-
const prefix = alternate?.path ?? localePath("/", locale.code, i18n, routeContext);
|
|
72
|
-
const resolvedPath = resolveHref(prefix);
|
|
73
|
-
const href = alternate ? resolveLocaleAlternateUrl({ ...alternate, path: resolvedPath }, (candidate) => candidate) : resolvedPath;
|
|
74
|
-
const count = pageCounts.get(locale.code) ?? 0;
|
|
75
|
-
const display = locale.nativeName ? `${locale.nativeName} (${locale.code})` : locale.name ? `${locale.name} (${locale.code})` : locale.code;
|
|
76
|
-
const suffix = isDefault ? "content included below" : "visit this language for content";
|
|
77
|
-
lines.push(normalizeLink({
|
|
78
|
-
title: display,
|
|
79
|
-
href,
|
|
80
|
-
description: `${count} pages; ${suffix}.`
|
|
81
|
-
}));
|
|
82
|
-
}
|
|
83
|
-
return lines;
|
|
84
|
-
}
|
|
85
64
|
export function formatLlmsTxtPageLink(page) {
|
|
86
65
|
const description = page.description?.trim().replace(RE_INLINE_WHITESPACE, " ");
|
|
87
66
|
const normalizedTitle = page.title ? normalizeInlineText(page.title) : "";
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { RuntimeI18nConfig, RuntimeRouteContext } from './server/utils/i18n.js';
|
|
2
|
+
export { resolveLocaleFromRoute } from './server/utils/i18n.js';
|
|
3
|
+
export declare function formatAvailableLanguagesSection(i18n: RuntimeI18nConfig, pageCounts: Map<string, number>, resolveHref?: (pathname: string) => string, routeContext?: RuntimeRouteContext): string[];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { resolveLocaleAlternateUrl } from "./i18n-url.js";
|
|
2
|
+
import { formatLlmsTxtPageLink } from "./llms-txt-format.js";
|
|
3
|
+
import { computeLocaleAlternates, localePath } from "./server/utils/i18n.js";
|
|
4
|
+
export { resolveLocaleFromRoute } from "./server/utils/i18n.js";
|
|
5
|
+
export function formatAvailableLanguagesSection(i18n, pageCounts, resolveHref = (pathname) => pathname, routeContext = {}) {
|
|
6
|
+
const lines = ["## Available Languages on Website", ""];
|
|
7
|
+
const rootAlternates = new Map(computeLocaleAlternates("/", i18n, routeContext).map((alternate) => [alternate.code, alternate]));
|
|
8
|
+
for (const locale of i18n.locales) {
|
|
9
|
+
const isDefault = locale.code === i18n.defaultLocale;
|
|
10
|
+
const alternate = rootAlternates.get(locale.code);
|
|
11
|
+
const prefix = alternate?.path ?? localePath("/", locale.code, i18n, routeContext);
|
|
12
|
+
const resolvedPath = resolveHref(prefix);
|
|
13
|
+
const href = alternate ? resolveLocaleAlternateUrl({ ...alternate, path: resolvedPath }, (candidate) => candidate) : resolvedPath;
|
|
14
|
+
const count = pageCounts.get(locale.code) ?? 0;
|
|
15
|
+
const display = locale.nativeName ? `${locale.nativeName} (${locale.code})` : locale.name ? `${locale.name} (${locale.code})` : locale.code;
|
|
16
|
+
const suffix = isDefault ? "content included below" : "visit this language for content";
|
|
17
|
+
lines.push(formatLlmsTxtPageLink({
|
|
18
|
+
pathname: href,
|
|
19
|
+
title: display,
|
|
20
|
+
href,
|
|
21
|
+
description: `${count} pages; ${suffix}.`
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
return lines;
|
|
25
|
+
}
|
|
@@ -3,12 +3,11 @@ import { getRequestURL } from "#nuxtseo/h3";
|
|
|
3
3
|
import { useRuntimeConfig } from "#nuxtseo/nitro";
|
|
4
4
|
import { getSiteConfig } from "#site-config/server/composables/getSiteConfig";
|
|
5
5
|
import { withSiteTrailingSlash, withSiteUrl } from "#site-config/server/composables/utils";
|
|
6
|
-
import {
|
|
6
|
+
import { formatLlmsTxtPageLink, normalizeLlmsTxtConfig } from "./llms-txt-format.js";
|
|
7
7
|
import { toMarkdownPath } from "./markdown-path.js";
|
|
8
8
|
import { normalizePersistedRoute, toDeployedRoute, toLogicalRoute } from "./route-path.js";
|
|
9
9
|
import { queryPages } from "./server/db/queries.js";
|
|
10
10
|
import { logger } from "./server/logger.js";
|
|
11
|
-
import { resolveLocaleFromRoute } from "./server/utils/i18n.js";
|
|
12
11
|
import { fetchSitemapUrls } from "./server/utils/sitemap.js";
|
|
13
12
|
export { normalizeLlmsTxtConfig };
|
|
14
13
|
function hasRuntimeMarkdownHandler(pathname) {
|
|
@@ -123,6 +122,7 @@ export async function buildLlmsTxt(event) {
|
|
|
123
122
|
const siteConfig = getSiteConfig(event);
|
|
124
123
|
const llmsTxtConfig = aiReadyConfig.llmsTxt;
|
|
125
124
|
const i18n = aiReadyConfig.i18n;
|
|
125
|
+
const i18nRuntime = i18n ? { _tag: "enabled", config: i18n, ...await import("./llms-txt-i18n.js") } : { _tag: "disabled" };
|
|
126
126
|
const i18nContext = { host: getRequestURL(event).host };
|
|
127
127
|
const baseURL = runtimeConfig.app.baseURL;
|
|
128
128
|
const resolvePath = (path) => withSiteTrailingSlash(event, toDeployedRoute(path, baseURL));
|
|
@@ -194,7 +194,7 @@ Canonical Origin: ${canonicalSiteUrl}`);
|
|
|
194
194
|
const other = [];
|
|
195
195
|
for (const pathname of sitemapPaths) {
|
|
196
196
|
if (!seenPaths.has(pathname) && !errorSet.has(pathname)) {
|
|
197
|
-
const locale =
|
|
197
|
+
const locale = i18nRuntime._tag === "enabled" ? i18nRuntime.resolveLocaleFromRoute(pathname, i18nRuntime.config, i18nContext).locale : void 0;
|
|
198
198
|
other.push({ pathname, locale });
|
|
199
199
|
seenPaths.add(pathname);
|
|
200
200
|
}
|
|
@@ -213,24 +213,24 @@ Canonical Origin: ${canonicalSiteUrl}`);
|
|
|
213
213
|
}
|
|
214
214
|
for (const page of [...prerendered, ...other])
|
|
215
215
|
page.href = resolvePageHref(page.pathname);
|
|
216
|
-
if (
|
|
216
|
+
if (i18nRuntime._tag === "enabled") {
|
|
217
217
|
const pageCounts = /* @__PURE__ */ new Map();
|
|
218
|
-
for (const locale of
|
|
218
|
+
for (const locale of i18nRuntime.config.locales) pageCounts.set(locale.code, 0);
|
|
219
219
|
for (const p of [...prerendered, ...other]) {
|
|
220
|
-
const code = p.locale || resolveLocaleFromRoute(p.pathname,
|
|
220
|
+
const code = p.locale || i18nRuntime.resolveLocaleFromRoute(p.pathname, i18nRuntime.config, i18nContext).locale;
|
|
221
221
|
pageCounts.set(code, (pageCounts.get(code) ?? 0) + 1);
|
|
222
222
|
}
|
|
223
|
-
parts.push(...formatAvailableLanguagesSection(
|
|
223
|
+
parts.push(...i18nRuntime.formatAvailableLanguagesSection(i18nRuntime.config, pageCounts, resolvePageHref, i18nContext));
|
|
224
224
|
parts.push("");
|
|
225
225
|
}
|
|
226
226
|
const isDefaultLocale = (item) => {
|
|
227
|
-
if (
|
|
227
|
+
if (i18nRuntime._tag === "disabled")
|
|
228
228
|
return true;
|
|
229
|
-
const code = item.locale || resolveLocaleFromRoute(item.pathname,
|
|
230
|
-
return code ===
|
|
229
|
+
const code = item.locale || i18nRuntime.resolveLocaleFromRoute(item.pathname, i18nRuntime.config, i18nContext).locale;
|
|
230
|
+
return code === i18nRuntime.config.defaultLocale;
|
|
231
231
|
};
|
|
232
|
-
const filteredPrerendered =
|
|
233
|
-
const filteredOther =
|
|
232
|
+
const filteredPrerendered = i18nRuntime._tag === "enabled" ? prerendered.filter(isDefaultLocale) : prerendered;
|
|
233
|
+
const filteredOther = i18nRuntime._tag === "enabled" ? other.filter(isDefaultLocale) : other;
|
|
234
234
|
const sortedPrerendered = sortPagesByPath(filteredPrerendered);
|
|
235
235
|
const sortedOther = sortPagesByPath(filteredOther);
|
|
236
236
|
if (sortedPrerendered.length > 0 && sortedOther.length > 0) {
|
|
@@ -2,17 +2,35 @@ import { createNitroRouteRuleMatcher } from "nuxtseo-shared/server";
|
|
|
2
2
|
import { appendHeader, createError, defineEventHandler, getHeader, getRequestURL, getResponseHeader, sendRedirect, setHeader } from "#nuxtseo/h3";
|
|
3
3
|
import { useRuntimeConfig } from "#nuxtseo/nitro";
|
|
4
4
|
import { withSiteUrl } from "#site-config/server/composables/utils";
|
|
5
|
+
import { resolveLocaleAlternateUrl } from "../../i18n-url.js";
|
|
5
6
|
import { toMarkdownPath } from "../../markdown-path.js";
|
|
6
7
|
import { toDeployedRoute } from "../../route-path.js";
|
|
7
8
|
import { logger } from "../logger.js";
|
|
9
|
+
import { setStatusAwareLinkHeader } from "../plugins/link-header.js";
|
|
8
10
|
import { CONTENT_NEGOTIATION_VARY, resolveContentNegotiation } from "../utils/content-negotiation.js";
|
|
9
|
-
import { computeLocaleAlternates,
|
|
11
|
+
import { computeLocaleAlternates, resolveLocaleFromRoute } from "../utils/i18n.js";
|
|
10
12
|
import { buildLinkHeader } from "../utils/link-header.js";
|
|
11
13
|
import { getMarkdownRenderInfo } from "../utils/markdown-request.js";
|
|
12
14
|
const INTERNAL_HEADER = "x-ai-ready-internal";
|
|
15
|
+
function setLinkHeader(event, path, variant, config, resolveUrl, routeContext) {
|
|
16
|
+
setHeader(event, "link", buildLinkHeader(path, variant, config, resolveUrl, routeContext));
|
|
17
|
+
}
|
|
18
|
+
function setStatusAwareHeader(event, path, variant, config, resolveUrl, routeContext) {
|
|
19
|
+
const successHeader = buildLinkHeader(path, variant, config, resolveUrl, routeContext);
|
|
20
|
+
if (!config.i18n) {
|
|
21
|
+
setHeader(event, "link", successHeader);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const safeHeader = buildLinkHeader(path, variant, { ...config, i18n: null }, resolveUrl, routeContext);
|
|
25
|
+
setStatusAwareLinkHeader(event, safeHeader, successHeader);
|
|
26
|
+
}
|
|
13
27
|
function setNegotiationHeaders(event, path, config, resolveUrl, routeContext) {
|
|
14
28
|
appendHeader(event, "vary", CONTENT_NEGOTIATION_VARY);
|
|
15
|
-
|
|
29
|
+
setLinkHeader(event, path, "html", config, resolveUrl, routeContext);
|
|
30
|
+
}
|
|
31
|
+
function setStatusAwareNegotiationHeaders(event, path, config, resolveUrl, routeContext) {
|
|
32
|
+
appendHeader(event, "vary", CONTENT_NEGOTIATION_VARY);
|
|
33
|
+
setStatusAwareHeader(event, path, "html", config, resolveUrl, routeContext);
|
|
16
34
|
}
|
|
17
35
|
function setUncacheableHeaders(event) {
|
|
18
36
|
setHeader(event, "cache-control", "private, no-store");
|
|
@@ -29,7 +47,7 @@ function setUncacheableHeaders(event) {
|
|
|
29
47
|
}
|
|
30
48
|
function setMarkdownHeaders(event, path, config, resolveUrl, routeContext) {
|
|
31
49
|
setHeader(event, "content-type", "text/markdown; charset=utf-8");
|
|
32
|
-
|
|
50
|
+
setLinkHeader(event, path, "markdown", config, resolveUrl, routeContext);
|
|
33
51
|
if (config.markdownCacheHeaders) {
|
|
34
52
|
const { maxAge, swr } = config.markdownCacheHeaders;
|
|
35
53
|
const cacheControl = swr ? `public, max-age=${maxAge}, stale-while-revalidate=${maxAge}` : `public, max-age=${maxAge}`;
|
|
@@ -100,9 +118,9 @@ export default defineEventHandler(async (event) => {
|
|
|
100
118
|
const canonicalUrl = resolveUrl(path);
|
|
101
119
|
if (negotiation === "html") {
|
|
102
120
|
if (contentNegotiation._tag === "enabled")
|
|
103
|
-
|
|
121
|
+
setStatusAwareNegotiationHeaders(event, path, config, resolveUrl, routeContext);
|
|
104
122
|
else
|
|
105
|
-
|
|
123
|
+
setStatusAwareHeader(event, path, "html", config, resolveUrl, routeContext);
|
|
106
124
|
return;
|
|
107
125
|
}
|
|
108
126
|
if (!isExplicit) {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { getHeader, getResponseStatus, setHeader } from "#nuxtseo/h3";
|
|
2
|
+
import { defineNitroPlugin } from "#nuxtseo/nitro";
|
|
3
|
+
const STATUS_AWARE_LINK_HEADER = "nuxt-ai-ready:status-aware-link-header";
|
|
4
|
+
const ERROR_LINK_HEADER = "x-nuxt-ai-ready-error-link";
|
|
5
|
+
export function setStatusAwareLinkHeader(event, safeHeader, successHeader) {
|
|
6
|
+
event.context[STATUS_AWARE_LINK_HEADER] = {
|
|
7
|
+
error: safeHeader,
|
|
8
|
+
success: successHeader ?? safeHeader
|
|
9
|
+
};
|
|
10
|
+
setHeader(event, "link", safeHeader);
|
|
11
|
+
}
|
|
12
|
+
export default defineNitroPlugin((nitro) => {
|
|
13
|
+
nitro.hooks.hook("error", (_error, { event }) => {
|
|
14
|
+
if (!event)
|
|
15
|
+
return;
|
|
16
|
+
const header = event.context[STATUS_AWARE_LINK_HEADER];
|
|
17
|
+
if (header)
|
|
18
|
+
event.node.req.headers[ERROR_LINK_HEADER] = header.error;
|
|
19
|
+
});
|
|
20
|
+
nitro.hooks.hook("beforeResponse", (event) => {
|
|
21
|
+
const header = event.context[STATUS_AWARE_LINK_HEADER];
|
|
22
|
+
if (!header)
|
|
23
|
+
return;
|
|
24
|
+
setHeader(event, "link", getResponseStatus(event) >= 400 ? header.error : header.success);
|
|
25
|
+
});
|
|
26
|
+
nitro.hooks.hook("render:response", (response, { event }) => {
|
|
27
|
+
const header = event.context[STATUS_AWARE_LINK_HEADER];
|
|
28
|
+
const errorHeader = event.path.startsWith("/__nuxt_error") && getHeader(event, "x-nuxt-error") === "true" ? getHeader(event, ERROR_LINK_HEADER) : void 0;
|
|
29
|
+
if (errorHeader) {
|
|
30
|
+
response.headers ||= {};
|
|
31
|
+
response.headers.link = errorHeader;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (!header)
|
|
35
|
+
return;
|
|
36
|
+
response.headers ||= {};
|
|
37
|
+
const statusCode = response.statusCode ?? getResponseStatus(event);
|
|
38
|
+
response.headers.link = statusCode >= 400 ? header.error : header.success;
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useRuntimeConfig } from "#nuxtseo/nitro";
|
|
2
2
|
import { getPageLastmods, getSitemapLastCrawledAt, markSitemapSeeded, seedRoutes } from "../db/queries.js";
|
|
3
3
|
import { logger } from "../logger.js";
|
|
4
|
-
import { mapSitemapRoutes } from "../utils/sitemap.js";
|
|
4
|
+
import { mapSitemapRoutes } from "../utils/sitemap-routes.js";
|
|
5
5
|
const DIAGNOSTICS_KEY = "_aiReadySitemapDiagnostics";
|
|
6
6
|
function isDebug(event) {
|
|
7
7
|
return !!useRuntimeConfig(event)["nuxt-ai-ready"]?.debug;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export { computeLocaleAlternates, localePath, resolveLocaleAlternates, resolveLocaleFromRoute, } from '
|
|
1
|
+
import type { RuntimeI18nConfig } from 'nuxtseo-shared/i18n-runtime';
|
|
2
|
+
export { computeLocaleAlternates, localePath, resolveLocaleAlternates, resolveLocaleFromRoute, } from '#ai-ready-virtual/i18n-runtime.mjs';
|
|
3
3
|
export type { LocaleAlternate, LocaleAlternateResolution, LocalePages, RouteLocaleInfo, RuntimeI18nConfig, RuntimeLocale, RuntimeRouteContext, } from 'nuxtseo-shared/i18n-runtime';
|
|
4
|
-
/** Resolve a locale alternate against its configured domain or the current site. */
|
|
5
|
-
export declare function resolveLocaleAlternateUrl(alternate: Pick<LocaleAlternate, 'domain' | 'path'>, resolveUrl: (path: string) => string): string;
|
|
6
4
|
/** Get the runtime i18n config from runtimeConfig, or null when disabled. */
|
|
7
5
|
export declare function getRuntimeI18n(aiReadyConfig: {
|
|
8
6
|
i18n?: RuntimeI18nConfig | null;
|
|
@@ -3,15 +3,7 @@ export {
|
|
|
3
3
|
localePath,
|
|
4
4
|
resolveLocaleAlternates,
|
|
5
5
|
resolveLocaleFromRoute
|
|
6
|
-
} from "
|
|
7
|
-
export function resolveLocaleAlternateUrl(alternate, resolveUrl) {
|
|
8
|
-
const resolved = resolveUrl(alternate.path);
|
|
9
|
-
if (!alternate.domain)
|
|
10
|
-
return resolved;
|
|
11
|
-
const origin = /^[a-z][a-z\d+.-]*:\/\//i.test(alternate.domain) ? alternate.domain : `https://${alternate.domain}`;
|
|
12
|
-
const deployed = new URL(resolved, "http://nuxtseo.local");
|
|
13
|
-
return new URL(`${deployed.pathname}${deployed.search}${deployed.hash}`, origin).href;
|
|
14
|
-
}
|
|
6
|
+
} from "#ai-ready-virtual/i18n-runtime.mjs";
|
|
15
7
|
export function getRuntimeI18n(aiReadyConfig) {
|
|
16
8
|
return aiReadyConfig.i18n || null;
|
|
17
9
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { resolveLocaleAlternateUrl } from "../../i18n-url.js";
|
|
1
2
|
import { toMarkdownPath } from "../../markdown-path.js";
|
|
2
|
-
import { computeLocaleAlternates
|
|
3
|
+
import { computeLocaleAlternates } from "./i18n.js";
|
|
3
4
|
export function encodePathForHeader(path) {
|
|
4
5
|
return encodeURI(path);
|
|
5
6
|
}
|
|
@@ -5,7 +5,8 @@ import { batchIndexPages } from "./batchIndex.js";
|
|
|
5
5
|
import { checkAndHandleStale, STALE_CHECK_INTERVAL_MS } from "./checkStale.js";
|
|
6
6
|
import { resolveCronPlan, resolveSitemapIntervalMinutes } from "./cron-plan.js";
|
|
7
7
|
import { syncToIndexNow } from "./indexnow.js";
|
|
8
|
-
import { crawlSitemapByRoute, getSitemapsFromConfig
|
|
8
|
+
import { crawlSitemapByRoute, getSitemapsFromConfig } from "./sitemap.js";
|
|
9
|
+
import { mapSitemapRoutes } from "./sitemap-routes.js";
|
|
9
10
|
export async function runCron(event, options) {
|
|
10
11
|
if (import.meta.dev)
|
|
11
12
|
return {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface SitemapRouteSource {
|
|
2
|
+
loc: string;
|
|
3
|
+
_path?: {
|
|
4
|
+
pathname: string;
|
|
5
|
+
} | null;
|
|
6
|
+
}
|
|
7
|
+
/** Normalize sitemap URL records into the route map used by the page store. */
|
|
8
|
+
export declare function mapSitemapRoutes<T extends SitemapRouteSource>(urls: readonly T[]): Map<string, T>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function mapSitemapRoutes(urls) {
|
|
2
|
+
const routeToUrl = /* @__PURE__ */ new Map();
|
|
3
|
+
for (const url of urls) {
|
|
4
|
+
const route = url._path?.pathname ?? (url.loc.startsWith("/") ? url.loc.split("?")[0] ?? url.loc : new URL(url.loc).pathname);
|
|
5
|
+
if (!route.includes("."))
|
|
6
|
+
routeToUrl.set(route, url);
|
|
7
|
+
}
|
|
8
|
+
return routeToUrl;
|
|
9
|
+
}
|
|
@@ -8,12 +8,6 @@ export interface SitemapConfig {
|
|
|
8
8
|
name: string;
|
|
9
9
|
route: string;
|
|
10
10
|
}
|
|
11
|
-
export interface SitemapRouteSource {
|
|
12
|
-
loc: string;
|
|
13
|
-
_path?: {
|
|
14
|
-
pathname: string;
|
|
15
|
-
} | null;
|
|
16
|
-
}
|
|
17
11
|
interface SitemapCrawlResultBase {
|
|
18
12
|
urls: SitemapUrl[];
|
|
19
13
|
documentsAttempted: number;
|
|
@@ -39,8 +33,6 @@ export declare function getSitemapsFromConfig(event?: H3Event): SitemapConfig[];
|
|
|
39
33
|
* Check if site has multiple sitemaps configured
|
|
40
34
|
*/
|
|
41
35
|
export declare function hasMultipleSitemaps(event: H3Event): boolean;
|
|
42
|
-
/** Normalize sitemap URL records into the route map used by the page store. */
|
|
43
|
-
export declare function mapSitemapRoutes<T extends SitemapRouteSource>(urls: readonly T[]): Map<string, T>;
|
|
44
36
|
/**
|
|
45
37
|
* Execute one bounded sitemap traversal round. Partial results include the
|
|
46
38
|
* exact pending frontier and committed document set needed by the next round.
|
|
@@ -39,15 +39,6 @@ export function hasMultipleSitemaps(event) {
|
|
|
39
39
|
const sitemaps = getSitemapsFromConfig(event);
|
|
40
40
|
return sitemaps.length > 1;
|
|
41
41
|
}
|
|
42
|
-
export function mapSitemapRoutes(urls) {
|
|
43
|
-
const routeToUrl = /* @__PURE__ */ new Map();
|
|
44
|
-
for (const url of urls) {
|
|
45
|
-
const route = url._path?.pathname ?? (url.loc.startsWith("/") ? url.loc.split("?")[0] ?? url.loc : new URL(url.loc).pathname);
|
|
46
|
-
if (!route.includes("."))
|
|
47
|
-
routeToUrl.set(route, url);
|
|
48
|
-
}
|
|
49
|
-
return routeToUrl;
|
|
50
|
-
}
|
|
51
42
|
function normalizeUrl(entry) {
|
|
52
43
|
return {
|
|
53
44
|
loc: entry.loc,
|
|
@@ -177,6 +177,15 @@ declare module '#ai-ready-virtual/logger.mjs' {
|
|
|
177
177
|
export const logger: ConsolaInstance
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
declare module '#ai-ready-virtual/i18n-runtime.mjs' {
|
|
181
|
+
export {
|
|
182
|
+
computeLocaleAlternates,
|
|
183
|
+
localePath,
|
|
184
|
+
resolveLocaleAlternates,
|
|
185
|
+
resolveLocaleFromRoute,
|
|
186
|
+
} from 'nuxtseo-shared/i18n-runtime'
|
|
187
|
+
}
|
|
188
|
+
|
|
180
189
|
declare module '#ai-ready-virtual/db-provider.mjs' {
|
|
181
190
|
import type { H3Event } from '#nuxtseo/h3'
|
|
182
191
|
import type { DrizzleDatabase } from '#ai-ready/server/db/drizzle/client'
|
|
@@ -480,11 +489,16 @@ function collectPagePaths(routes, parentPath = "") {
|
|
|
480
489
|
return paths;
|
|
481
490
|
}, /* @__PURE__ */ new Map());
|
|
482
491
|
}
|
|
492
|
+
function isUnlocalizedLocalePage(page) {
|
|
493
|
+
return page?._tag === "unlocalized" && typeof page.path === "string";
|
|
494
|
+
}
|
|
483
495
|
function materializeI18nPages(i18n, routes) {
|
|
484
496
|
if (!i18n.pages)
|
|
485
497
|
return void 0;
|
|
486
498
|
const pagePaths = collectPagePaths(routes);
|
|
487
499
|
return Object.fromEntries(Object.entries(i18n.pages).map(([pageName, pageLocales]) => {
|
|
500
|
+
if (isUnlocalizedLocalePage(pageLocales))
|
|
501
|
+
return [pageName, pageLocales];
|
|
488
502
|
const defaultPath = pageLocales?.[i18n.defaultLocale];
|
|
489
503
|
const fallbackPath = typeof defaultPath === "string" ? defaultPath : pagePaths.get(pageName);
|
|
490
504
|
const locales = Object.fromEntries(i18n.locales.flatMap((locale) => {
|
|
@@ -881,6 +895,7 @@ const module$1 = defineNuxtModule({
|
|
|
881
895
|
async setup(config, nuxt) {
|
|
882
896
|
const moduleEntryUrl = resolveModuleEntryUrl(import.meta.url);
|
|
883
897
|
const resolveFromModule = createRequire(moduleEntryUrl);
|
|
898
|
+
const nuxtSeoSharedI18nRuntimePath = resolveFromModule.resolve("nuxtseo-shared/i18n-runtime");
|
|
884
899
|
const nuxtSeoSharedUtilsPath = resolveFromModule.resolve("nuxtseo-shared/utils");
|
|
885
900
|
const { resolve } = createResolver(moduleEntryUrl);
|
|
886
901
|
const { version } = await readPackageJSON(resolve("../package.json"));
|
|
@@ -1367,6 +1382,15 @@ ${details}`);
|
|
|
1367
1382
|
}
|
|
1368
1383
|
}
|
|
1369
1384
|
nitroConfig.virtual = nitroConfig.virtual || {};
|
|
1385
|
+
nitroConfig.virtual["#ai-ready-virtual/i18n-runtime.mjs"] = () => {
|
|
1386
|
+
const runtimeConfig = nitroConfig.runtimeConfig?.["nuxt-ai-ready"];
|
|
1387
|
+
return runtimeConfig?.i18n ? `export { computeLocaleAlternates, localePath, resolveLocaleAlternates, resolveLocaleFromRoute } from ${JSON.stringify(nuxtSeoSharedI18nRuntimePath)}` : `
|
|
1388
|
+
const unavailable = () => {
|
|
1389
|
+
throw new Error('[nuxt-ai-ready] i18n runtime called without i18n configuration.')
|
|
1390
|
+
}
|
|
1391
|
+
export { unavailable as computeLocaleAlternates, unavailable as localePath, unavailable as resolveLocaleAlternates, unavailable as resolveLocaleFromRoute }
|
|
1392
|
+
`;
|
|
1393
|
+
};
|
|
1370
1394
|
nitroConfig.virtual["#ai-ready-virtual/site-tools.mjs"] = `export default ${JSON.stringify(siteToolsConfig)}`;
|
|
1371
1395
|
nitroConfig.virtual["#ai-ready-virtual/agent-skills.mjs"] = agentSkillsResult._tag === "Enabled" ? `export const agentSkillsIndex = ${JSON.stringify(agentSkillsResult.index)}
|
|
1372
1396
|
export const localAgentSkillArtifacts = ${JSON.stringify(agentSkillsResult.localArtifacts)}` : "export const agentSkillsIndex = null\nexport const localAgentSkillArtifacts = {}";
|
|
@@ -1471,6 +1495,7 @@ export async function lookupContentPage(event, path) {
|
|
|
1471
1495
|
middleware: true,
|
|
1472
1496
|
handler: resolve("./runtime/server/middleware/markdown")
|
|
1473
1497
|
});
|
|
1498
|
+
addServerPlugin(resolve("./runtime/server/plugins/link-header"));
|
|
1474
1499
|
addPlugin({
|
|
1475
1500
|
mode: "server",
|
|
1476
1501
|
src: resolve("./runtime/app/plugins/md-alternate.server")
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-ai-ready",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.7.
|
|
4
|
+
"version": "1.7.13",
|
|
5
5
|
"description": "Best practice AI & LLM discoverability for Nuxt sites.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -62,18 +62,18 @@
|
|
|
62
62
|
}
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@mdream/js": "^1.
|
|
65
|
+
"@mdream/js": "^1.6.2",
|
|
66
66
|
"@nuxt/kit": "^4.5.2",
|
|
67
67
|
"citty": "^0.2.2",
|
|
68
68
|
"consola": "^3.4.2",
|
|
69
69
|
"defu": "^6.1.7",
|
|
70
70
|
"drizzle-orm": "^0.45.2",
|
|
71
|
-
"mdream": "^1.
|
|
72
|
-
"nuxt-site-config": "^4.2.
|
|
73
|
-
"nuxtseo-shared": "^5.3.
|
|
71
|
+
"mdream": "^1.6.2",
|
|
72
|
+
"nuxt-site-config": "^4.2.2",
|
|
73
|
+
"nuxtseo-shared": "^5.3.12",
|
|
74
74
|
"pathe": "^2.0.3",
|
|
75
75
|
"pkg-types": "^2.3.1",
|
|
76
|
-
"site-config-stack": "^4.2.
|
|
76
|
+
"site-config-stack": "^4.2.2",
|
|
77
77
|
"sitemapd": "^0.2.2",
|
|
78
78
|
"ufo": "^1.6.4",
|
|
79
79
|
"uncrypto": "^0.1.3",
|
|
@@ -89,13 +89,13 @@
|
|
|
89
89
|
"@nuxtjs/eslint-config-typescript": "^12.1.0",
|
|
90
90
|
"@nuxtjs/mcp-toolkit": "^0.18.1",
|
|
91
91
|
"@nuxtjs/robots": "^6.1.4",
|
|
92
|
-
"@nuxtjs/sitemap": "^8.3.
|
|
92
|
+
"@nuxtjs/sitemap": "^8.3.4",
|
|
93
93
|
"@types/better-sqlite3": "^9.6.0",
|
|
94
94
|
"@vitest/coverage-v8": "^4.1.10",
|
|
95
95
|
"@vue/test-utils": "^2.4.11",
|
|
96
96
|
"@vueuse/nuxt": "^14.4.0",
|
|
97
97
|
"better-sqlite3": "^13.0.3",
|
|
98
|
-
"bumpp": "^12.2.
|
|
98
|
+
"bumpp": "^12.2.1",
|
|
99
99
|
"eslint": "^10.8.1",
|
|
100
100
|
"eslint-plugin-harlanzw": "^0.17.1",
|
|
101
101
|
"execa": "^10.0.1",
|
|
@@ -103,8 +103,8 @@
|
|
|
103
103
|
"happy-dom": "^20.11.2",
|
|
104
104
|
"nitropack": "^2.13.4",
|
|
105
105
|
"nuxt": "^4.5.2",
|
|
106
|
-
"nuxt-site-config": "^4.2.
|
|
107
|
-
"nuxtseo-layer-devtools": "^5.3.
|
|
106
|
+
"nuxt-site-config": "^4.2.2",
|
|
107
|
+
"nuxtseo-layer-devtools": "^5.3.12",
|
|
108
108
|
"playwright": "^1.62.1",
|
|
109
109
|
"playwright-core": "^1.62.1",
|
|
110
110
|
"tinyglobby": "^0.2.17",
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
"vue": "^3.5.41",
|
|
115
115
|
"vue-router": "^5.2.0",
|
|
116
116
|
"vue-tsc": "^3.3.9",
|
|
117
|
-
"wrangler": "^4.
|
|
117
|
+
"wrangler": "^4.122.0",
|
|
118
118
|
"zod": "^4.4.3"
|
|
119
119
|
},
|
|
120
120
|
"scripts": {
|