nuxt-ai-ready 1.7.0 → 1.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.d.mts CHANGED
@@ -3,6 +3,7 @@ import { LlmsTxtConfig, ContentNegotiationPolicy, ModuleOptions } from '../dist/
3
3
  export { AgentSkillConfig, AgentSkillsConfig, AgentSkillsIndex, AgentSkillsIndexEntry, ApiCatalogConfig, ApiCatalogEntry, ApiCatalogLinkTarget, ApiCatalogLinks, ExternalAgentSkillConfig, LocalAgentSkillConfig, McpServerCardConfig, ModuleOptions } from '../dist/runtime/types.js';
4
4
  import { ResolvedWebMcpToolsConfig } from '../dist/runtime/site-tool-config.js';
5
5
  export { GetPageMarkdownToolOptions, ListPagesToolOptions, McpSiteToolAttachmentOptions, SearchPagesToolOptions, SiteToolOptions, SiteToolsConfig, WebMcpSiteToolAttachmentOptions } from '../dist/runtime/site-tool-config.js';
6
+ import { AiCatalog } from '../dist/runtime/server/utils/discovery-response.js';
6
7
 
7
8
  interface ParsedMarkdownResult {
8
9
  markdown: string;
@@ -34,6 +35,12 @@ interface ResolvedWebMcpConfig {
34
35
  exposedTo?: string[];
35
36
  }
36
37
 
38
+ declare function resolveAiCatalog(input: {
39
+ siteUrl: string;
40
+ serverCardName: string;
41
+ serverCardUrl: string;
42
+ }): AiCatalog;
43
+
37
44
  interface ModuleHooks {
38
45
  /**
39
46
  * Hook called when page markdown is generated during prerendering.
@@ -90,6 +97,11 @@ interface ModulePublicRuntimeConfig {
90
97
  }>;
91
98
  } | null;
92
99
  ftsTokenizer?: string;
100
+ aiCatalog?: {
101
+ cacheMaxAge: number;
102
+ document: ReturnType<typeof resolveAiCatalog>;
103
+ etag: string;
104
+ };
93
105
  apiCatalog?: ResolvedApiCatalogConfig;
94
106
  }
95
107
  /** Runtime config exposed to the browser, only set when WebMCP is enabled. */
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "nuxt": ">=4.0.0"
5
5
  },
6
6
  "configKey": "aiReady",
7
- "version": "1.7.0",
7
+ "version": "1.7.2",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.3",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createHash, randomBytes } from 'node:crypto';
2
2
  import { mkdir, writeFile, appendFile, stat, readdir, realpath, readFile, access } from 'node:fs/promises';
3
+ import { createRequire } from 'node:module';
3
4
  import { join, dirname, relative, resolve, isAbsolute, sep } from 'node:path';
4
5
  import { useLogger, useNuxt, hasNuxtModule, resolveFiles, addTypeTemplate, addTemplate, defineNuxtModule, createResolver, addServerPlugin, addServerHandler, extendRouteRules, addPlugin, addImports } from '@nuxt/kit';
5
6
  import defu from 'defu';
@@ -16,9 +17,9 @@ import { initSchema, computeContentHash, insertPage, queryAllPages, exportDbDump
16
17
  import { comparePageHashes, submitToIndexNowShared } from '../dist/runtime/server/utils/indexnow-shared.js';
17
18
  import { buildLlmsFullTxtHeader, formatPageForLlmsFullTxt } from '../dist/runtime/server/utils/llms-full.js';
18
19
  import { parseDocument } from 'yaml';
20
+ import { MCP_SERVER_CARD_MEDIA_TYPE, AI_CATALOG_MEDIA_TYPE } from '../dist/runtime/server/utils/discovery-response.js';
19
21
  import { isAbsolute as isAbsolute$1, join as join$1 } from 'pathe';
20
22
  import { resolveI18nConfig } from 'nuxtseo-shared/i18n';
21
- import { createRequire } from 'node:module';
22
23
 
23
24
  const logger = useLogger("nuxt-ai-ready");
24
25
 
@@ -842,6 +843,23 @@ async function resolveAgentSkillsConfig(config, rootDir) {
842
843
  };
843
844
  }
844
845
 
846
+ const AI_CATALOG_PATH = "/.well-known/ai-catalog.json";
847
+ function resolveAiCatalog(input) {
848
+ const hostname = new URL(input.siteUrl).hostname;
849
+ const serverName = input.serverCardName.split("/").at(-1);
850
+ return {
851
+ specVersion: "1.0",
852
+ entries: [{
853
+ identifier: `urn:air:${hostname}:mcp:${serverName}`,
854
+ type: MCP_SERVER_CARD_MEDIA_TYPE,
855
+ url: input.serverCardUrl
856
+ }]
857
+ };
858
+ }
859
+ function createAiCatalogEtag(catalog) {
860
+ return `"${createHash("sha256").update(JSON.stringify(catalog)).digest("hex")}"`;
861
+ }
862
+
845
863
  const API_CATALOG_PATH = "/.well-known/api-catalog";
846
864
  const API_CATALOG_PROFILE = "https://www.rfc-editor.org/info/rfc9727";
847
865
  const API_CATALOG_MEDIA_TYPE = `application/linkset+json; profile="${API_CATALOG_PROFILE}"`;
@@ -1125,7 +1143,6 @@ function resolveMcpToolkitState(input) {
1125
1143
  }
1126
1144
 
1127
1145
  const MCP_SERVER_CARD_SCHEMA = "https://static.modelcontextprotocol.io/schemas/v1/server-card.schema.json";
1128
- const MCP_SERVER_CARD_MEDIA_TYPE = "application/mcp-server-card+json";
1129
1146
  const MCP_SERVER_CARD_NAME_PATTERN = /^[a-z\d.-]+\/[a-z\d._-]+$/i;
1130
1147
  const allowedConfigKeys = /* @__PURE__ */ new Set([
1131
1148
  "cacheMaxAge",
@@ -1213,17 +1230,28 @@ function resolveMcpServerCardName(input) {
1213
1230
  message: "Could not derive a valid MCP Server Card name; configure `aiReady.mcpServerCard.name`."
1214
1231
  };
1215
1232
  }
1216
- function parseLatestMcpProtocolVersion(source) {
1217
- const match = source.match(/LATEST_PROTOCOL_VERSION\s*=\s*['"]([^'"]+)['"]/);
1218
- if (!match?.[1]) {
1233
+ function parseSupportedMcpProtocolVersions(source) {
1234
+ const latestMatch = source.match(/LATEST_PROTOCOL_VERSION\s*=\s*['"]([^'"]+)['"]/);
1235
+ const supportedMatch = source.match(/SUPPORTED_PROTOCOL_VERSIONS\s*=\s*\[([\s\S]*?)\]/);
1236
+ if (!latestMatch?.[1] || !supportedMatch?.[1]) {
1219
1237
  return {
1220
1238
  _tag: "Invalid",
1221
- message: "Could not read LATEST_PROTOCOL_VERSION from the installed MCP SDK."
1239
+ message: "Could not read SUPPORTED_PROTOCOL_VERSIONS from the installed MCP SDK."
1222
1240
  };
1223
1241
  }
1224
- return { _tag: "Resolved", protocolVersion: match[1] };
1242
+ const versionEntryPattern = /(?:exports\.)?LATEST_PROTOCOL_VERSION|(['"])([^'"]+)\1/g;
1243
+ const entriesSource = supportedMatch[1];
1244
+ const protocolVersions = [...entriesSource.matchAll(versionEntryPattern)].map((match) => match[2] || latestMatch[1]).filter((version) => isNonEmptyString(version));
1245
+ const unsupportedSyntax = entriesSource.replace(versionEntryPattern, "").replace(/[\s,]/g, "");
1246
+ if (!protocolVersions.length || unsupportedSyntax) {
1247
+ return {
1248
+ _tag: "Invalid",
1249
+ message: unsupportedSyntax ? "The installed MCP SDK uses an unsupported protocol versions format." : "The installed MCP SDK does not declare any supported protocol versions."
1250
+ };
1251
+ }
1252
+ return { _tag: "Resolved", protocolVersions: [...new Set(protocolVersions)] };
1225
1253
  }
1226
- async function resolveInstalledMcpProtocolVersion(input) {
1254
+ async function resolveInstalledMcpProtocolVersions(input) {
1227
1255
  const resolutionBases = [.../* @__PURE__ */ new Set([...input.modulesDir, input.rootDir])];
1228
1256
  const sdkResolutionAttempts = resolutionBases.map(
1229
1257
  (base) => resolvePackageJSON("@nuxtjs/mcp-toolkit", { from: base }).then((toolkitPackagePath) => createRequire(toolkitPackagePath).resolve("@modelcontextprotocol/sdk/types.js"))
@@ -1233,9 +1261,9 @@ async function resolveInstalledMcpProtocolVersion(input) {
1233
1261
  if (resolved)
1234
1262
  return resolved.value;
1235
1263
  throw new AggregateError(results.map((result) => result.status === "rejected" ? result.reason : void 0), "Could not resolve the MCP Toolkit SDK.");
1236
- }).then((sdkTypesPath) => readFile(sdkTypesPath, "utf8")).then(parseLatestMcpProtocolVersion).catch((error) => ({
1264
+ }).then((sdkTypesPath) => readFile(sdkTypesPath, "utf8")).then(parseSupportedMcpProtocolVersions).catch((error) => ({
1237
1265
  _tag: "Invalid",
1238
- message: `Could not resolve the MCP SDK protocol version: ${error instanceof Error ? error.message : String(error)}`
1266
+ message: `Could not resolve the MCP SDK protocol versions: ${error instanceof Error ? error.message : String(error)}`
1239
1267
  }));
1240
1268
  }
1241
1269
  function resolveDescription(input) {
@@ -1262,7 +1290,7 @@ function resolveMcpServerCard(input) {
1262
1290
  card.remotes = [{
1263
1291
  type: "streamable-http",
1264
1292
  url: input.endpoint,
1265
- supportedProtocolVersions: [input.protocolVersion]
1293
+ supportedProtocolVersions: input.protocolVersions
1266
1294
  }];
1267
1295
  }
1268
1296
  if (title)
@@ -1457,6 +1485,8 @@ const module$1 = defineNuxtModule({
1457
1485
  };
1458
1486
  },
1459
1487
  async setup(config, nuxt) {
1488
+ const resolveFromModule = createRequire(import.meta.url);
1489
+ const nuxtSeoSharedUtilsPath = resolveFromModule.resolve("nuxtseo-shared/utils");
1460
1490
  const { resolve } = createResolver(import.meta.url);
1461
1491
  const { version } = await readPackageJSON(resolve("../package.json"));
1462
1492
  logger.level = config.debug || nuxt.options.debug ? 4 : 3;
@@ -1752,14 +1782,14 @@ ${details}`);
1752
1782
  });
1753
1783
  if (finalMcpServerCardNameResult._tag === "Invalid")
1754
1784
  throw new Error(`[nuxt-ai-ready] ${finalMcpServerCardNameResult.message}`);
1755
- const protocolVersionResult = await resolveInstalledMcpProtocolVersion({
1785
+ const protocolVersionsResult = await resolveInstalledMcpProtocolVersions({
1756
1786
  rootDir: nuxt.options.rootDir,
1757
1787
  modulesDir: nuxt.options.modulesDir
1758
1788
  });
1759
- if (protocolVersionResult._tag === "Invalid")
1760
- throw new Error(`[nuxt-ai-ready] ${protocolVersionResult.message}`);
1789
+ if (protocolVersionsResult._tag === "Invalid")
1790
+ throw new Error(`[nuxt-ai-ready] ${protocolVersionsResult.message}`);
1761
1791
  const card = resolveMcpServerCard({
1762
- protocolVersion: protocolVersionResult.protocolVersion,
1792
+ protocolVersions: protocolVersionsResult.protocolVersions,
1763
1793
  endpoint: siteConfig.url ? withSiteUrl(finalMcpToolkitState.route, { withBase: true }) : finalMcpToolkitState.route,
1764
1794
  name: finalMcpServerCardNameResult.name,
1765
1795
  toolkitTitle: configuredMcpTitle,
@@ -1780,6 +1810,7 @@ ${details}`);
1780
1810
  const handler = resolve("./runtime/server/routes/mcp-server-card");
1781
1811
  addServerHandler({ route: mcpServerCardRoute, method: "get", handler });
1782
1812
  addServerHandler({ route: mcpServerCardRoute, method: "head", handler });
1813
+ addServerHandler({ route: mcpServerCardRoute, method: "options", handler });
1783
1814
  extendRouteRules(mcpServerCardRoute, {
1784
1815
  sitemap: false,
1785
1816
  headers: {
@@ -1792,6 +1823,36 @@ ${details}`);
1792
1823
  "ETag": etag
1793
1824
  }
1794
1825
  });
1826
+ if (siteConfig.url) {
1827
+ const document = resolveAiCatalog({
1828
+ siteUrl: siteConfig.url,
1829
+ serverCardName: card.name,
1830
+ serverCardUrl: withSiteUrl(mcpServerCardRoute, { withBase: true })
1831
+ });
1832
+ const aiCatalogEtag = createAiCatalogEtag(document);
1833
+ const aiCatalogCacheMaxAge = mcpServerCardResult.config.cacheMaxAge;
1834
+ runtimeConfig.aiCatalog = {
1835
+ cacheMaxAge: aiCatalogCacheMaxAge,
1836
+ document,
1837
+ etag: aiCatalogEtag
1838
+ };
1839
+ const aiCatalogHandler = resolve("./runtime/server/routes/ai-catalog");
1840
+ addServerHandler({ route: AI_CATALOG_PATH, method: "get", handler: aiCatalogHandler });
1841
+ addServerHandler({ route: AI_CATALOG_PATH, method: "head", handler: aiCatalogHandler });
1842
+ addServerHandler({ route: AI_CATALOG_PATH, method: "options", handler: aiCatalogHandler });
1843
+ extendRouteRules(AI_CATALOG_PATH, {
1844
+ sitemap: false,
1845
+ headers: {
1846
+ "Access-Control-Allow-Headers": "Content-Type, If-None-Match",
1847
+ "Access-Control-Allow-Methods": "GET, HEAD",
1848
+ "Access-Control-Allow-Origin": "*",
1849
+ "Access-Control-Expose-Headers": "ETag",
1850
+ "Cache-Control": `public, max-age=${aiCatalogCacheMaxAge}`,
1851
+ "Content-Type": AI_CATALOG_MEDIA_TYPE,
1852
+ "ETag": aiCatalogEtag
1853
+ }
1854
+ });
1855
+ }
1795
1856
  });
1796
1857
  const sitemapConfig = nuxt.options.sitemap;
1797
1858
  const sitemapRouteRule = nuxt.options.nitro?.routeRules?.["/sitemap.xml"];
@@ -1952,7 +2013,7 @@ export async function readPageDataFromFilesystem() {
1952
2013
  nitroConfig.virtual["#ai-ready-virtual/page-data.mjs"] = `export const pages = []
1953
2014
  export const errorRoutes = []`;
1954
2015
  nitroConfig.virtual["#ai-ready-virtual/logger.mjs"] = `
1955
- import { createModuleLogger } from 'nuxtseo-shared/utils'
2016
+ import { createModuleLogger } from ${JSON.stringify(nuxtSeoSharedUtilsPath)}
1956
2017
  export const logger = createModuleLogger('nuxt-ai-ready', ${!!config.debug})
1957
2018
  `;
1958
2019
  const providerMap = {
@@ -1,3 +1,6 @@
1
- import type { ResolvedWebMcpConfig } from '../../utils/webmcp.js';
2
- export declare function setWebMcpDefaults(config: Pick<ResolvedWebMcpConfig, 'exposedTo'>): void;
3
- export declare function getWebMcpDefaults(): Readonly<Pick<ResolvedWebMcpConfig, 'exposedTo'>>;
1
+ interface WebMcpDefaults {
2
+ exposedTo?: string[];
3
+ }
4
+ export declare function setWebMcpDefaults(config: WebMcpDefaults): void;
5
+ export declare function getWebMcpDefaults(): Readonly<WebMcpDefaults>;
6
+ export {};
@@ -67,7 +67,7 @@ function notFoundMarkdown(canonicalUrl, path, config, resolveUrl) {
67
67
  ${body}`;
68
68
  }
69
69
  export default defineEventHandler(async (event) => {
70
- if (event.path.startsWith("/.well-known/agent-skills/"))
70
+ if (event.path.startsWith("/.well-known/"))
71
71
  return;
72
72
  if (getHeader(event, INTERNAL_HEADER))
73
73
  return;
@@ -21,7 +21,7 @@ function extractHeadingsFromMarkdown(markdown) {
21
21
  return headings;
22
22
  }
23
23
  export default defineEventHandler(async (event) => {
24
- if (event.path.startsWith("/.well-known/agent-skills/"))
24
+ if (event.path.startsWith("/.well-known/"))
25
25
  return;
26
26
  if (!import.meta.prerender) {
27
27
  return;
@@ -1,2 +1,2 @@
1
- declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, any>;
1
+ declare const _default: any;
2
2
  export default _default;
@@ -1,6 +1,6 @@
1
- import { assertMethod, createError, eventHandler, getRequestURL, setHeader } from "h3";
2
- import { useRuntimeConfig } from "nitropack/runtime";
3
1
  import { localAgentSkillArtifacts } from "#ai-ready-virtual/agent-skills.mjs";
2
+ import { assertMethod, createError, eventHandler, getRequestURL, setHeader } from "#nuxtseo/h3";
3
+ import { useRuntimeConfig } from "#nuxtseo/nitro";
4
4
  import { toLogicalRoute } from "../../route-path.js";
5
5
  export default eventHandler((event) => {
6
6
  assertMethod(event, ["GET", "HEAD"]);
@@ -1,2 +1,2 @@
1
- declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, any>;
1
+ declare const _default: any;
2
2
  export default _default;
@@ -1,5 +1,5 @@
1
- import { assertMethod, eventHandler, setHeader } from "h3";
2
1
  import { agentSkillsIndex } from "#ai-ready-virtual/agent-skills.mjs";
2
+ import { assertMethod, eventHandler, setHeader } from "#nuxtseo/h3";
3
3
  export default eventHandler((event) => {
4
4
  assertMethod(event, ["GET", "HEAD"]);
5
5
  setHeader(event, "Content-Type", "application/json; charset=utf-8");
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,24 @@
1
+ import { eventHandler, getHeader, setHeaders, setResponseStatus } from "#nuxtseo/h3";
2
+ import { useRuntimeConfig } from "#nuxtseo/nitro";
3
+ import { AI_CATALOG_MEDIA_TYPE, matchesDiscoveryEtag } from "../utils/discovery-response.js";
4
+ export default eventHandler((event) => {
5
+ const config = useRuntimeConfig(event)["nuxt-ai-ready"];
6
+ setHeaders(event, {
7
+ "Access-Control-Allow-Headers": "Content-Type, If-None-Match",
8
+ "Access-Control-Allow-Methods": "GET, HEAD",
9
+ "Access-Control-Allow-Origin": "*",
10
+ "Access-Control-Expose-Headers": "ETag",
11
+ "Cache-Control": `public, max-age=${config.aiCatalog.cacheMaxAge}`,
12
+ "Content-Type": AI_CATALOG_MEDIA_TYPE,
13
+ "ETag": config.aiCatalog.etag
14
+ });
15
+ if (event.method === "OPTIONS") {
16
+ setResponseStatus(event, 204);
17
+ return null;
18
+ }
19
+ if (matchesDiscoveryEtag(getHeader(event, "if-none-match"), config.aiCatalog.etag)) {
20
+ setResponseStatus(event, 304);
21
+ return null;
22
+ }
23
+ return config.aiCatalog.document;
24
+ });
@@ -1,4 +1,2 @@
1
- declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, {
2
- linkset: import("../../../utils/api-catalog.js").ResolvedApiCatalogLinksetEntry[];
3
- } | undefined>;
1
+ declare const _default: any;
4
2
  export default _default;
@@ -1,5 +1,5 @@
1
- import { assertMethod, defineEventHandler, setHeader } from "h3";
2
- import { useRuntimeConfig } from "nitropack/runtime";
1
+ import { assertMethod, defineEventHandler, setHeader } from "#nuxtseo/h3";
2
+ import { useRuntimeConfig } from "#nuxtseo/nitro";
3
3
  export default defineEventHandler((event) => {
4
4
  assertMethod(event, ["GET", "HEAD"]);
5
5
  const config = useRuntimeConfig(event)["nuxt-ai-ready"].apiCatalog;
@@ -1,3 +1,2 @@
1
- import type { McpServerCard } from '../../../utils/mcp-server-card.js';
2
- declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, McpServerCard | null>;
1
+ declare const _default: any;
3
2
  export default _default;
@@ -1,6 +1,6 @@
1
- import { eventHandler, getHeader, setHeaders, setResponseStatus } from "h3";
2
- import { useRuntimeConfig } from "nitropack/runtime";
3
- import { matchesMcpServerCardEtag, MCP_SERVER_CARD_MEDIA_TYPE } from "../../../utils/mcp-server-card";
1
+ import { eventHandler, getHeader, setHeaders, setResponseStatus } from "#nuxtseo/h3";
2
+ import { useRuntimeConfig } from "#nuxtseo/nitro";
3
+ import { matchesDiscoveryEtag, MCP_SERVER_CARD_MEDIA_TYPE } from "../utils/discovery-response.js";
4
4
  export default eventHandler((event) => {
5
5
  const config = useRuntimeConfig(event)["nuxt-ai-ready"];
6
6
  setHeaders(event, {
@@ -12,7 +12,11 @@ export default eventHandler((event) => {
12
12
  "Content-Type": MCP_SERVER_CARD_MEDIA_TYPE,
13
13
  "ETag": config.mcpServerCard.etag
14
14
  });
15
- if (matchesMcpServerCardEtag(getHeader(event, "if-none-match"), config.mcpServerCard.etag)) {
15
+ if (event.method === "OPTIONS") {
16
+ setResponseStatus(event, 204);
17
+ return null;
18
+ }
19
+ if (matchesDiscoveryEtag(getHeader(event, "if-none-match"), config.mcpServerCard.etag)) {
16
20
  setResponseStatus(event, 304);
17
21
  return null;
18
22
  }
@@ -0,0 +1,11 @@
1
+ export declare const AI_CATALOG_MEDIA_TYPE = "application/ai-catalog+json";
2
+ export declare const MCP_SERVER_CARD_MEDIA_TYPE = "application/mcp-server-card+json";
3
+ export interface AiCatalog {
4
+ specVersion: '1.0';
5
+ entries: Array<{
6
+ identifier: string;
7
+ type: typeof MCP_SERVER_CARD_MEDIA_TYPE;
8
+ url: string;
9
+ }>;
10
+ }
11
+ export declare function matchesDiscoveryEtag(requestHeader: string | undefined, etag: string): boolean;
@@ -0,0 +1,10 @@
1
+ export const AI_CATALOG_MEDIA_TYPE = "application/ai-catalog+json";
2
+ export const MCP_SERVER_CARD_MEDIA_TYPE = "application/mcp-server-card+json";
3
+ export function matchesDiscoveryEtag(requestHeader, etag) {
4
+ if (!requestHeader)
5
+ return false;
6
+ return requestHeader.split(",").some((candidate) => {
7
+ const normalized = candidate.trim().replace(/^W\//, "");
8
+ return normalized === "*" || normalized === etag;
9
+ });
10
+ }
@@ -1,5 +1,14 @@
1
- import type { RuntimeI18nConfig } from '../../../utils/i18n.js';
2
- export type { RuntimeI18nConfig } from '../../../utils/i18n.js';
1
+ /** Runtime-safe subset used for route locale resolution. */
2
+ export interface RuntimeI18nConfig {
3
+ defaultLocale: string;
4
+ strategy: 'no_prefix' | 'prefix_except_default' | 'prefix' | 'prefix_and_default';
5
+ locales: Array<{
6
+ code: string;
7
+ hreflang: string;
8
+ name?: string;
9
+ nativeName?: string;
10
+ }>;
11
+ }
3
12
  export interface LocaleAlternate {
4
13
  code: string;
5
14
  hreflang: string;
@@ -1,4 +1,4 @@
1
- import type { ModulePublicRuntimeConfig } from '../../../module.js';
1
+ import type { RuntimeI18nConfig } from './i18n.js';
2
2
  /**
3
3
  * Encode a URL path for safe inclusion in an HTTP header value.
4
4
  * HTTP header values must be ASCII-only per RFC 9110 §5.5, so paths containing
@@ -8,8 +8,14 @@ import type { ModulePublicRuntimeConfig } from '../../../module.js';
8
8
  */
9
9
  export declare function encodePathForHeader(path: string): string;
10
10
  type LinkUrlResolver = (path: string) => string;
11
+ interface LinkHeaderConfig {
12
+ apiCatalog?: {
13
+ href: string;
14
+ };
15
+ i18n?: RuntimeI18nConfig | null;
16
+ }
11
17
  /**
12
18
  * Build a comma-joined Link header value with the standard alternates plus i18n hreflang variants.
13
19
  */
14
- export declare function buildLinkHeader(path: string, variant: 'html' | 'markdown', config: ModulePublicRuntimeConfig, resolveUrl?: LinkUrlResolver): string;
20
+ export declare function buildLinkHeader(path: string, variant: 'html' | 'markdown', config: LinkHeaderConfig, resolveUrl?: LinkUrlResolver): string;
15
21
  export {};
@@ -1,6 +1,6 @@
1
1
  import type { ContentNegotiationResult } from '@mdream/js/negotiate';
2
2
  import type { H3Event } from '#nuxtseo/h3';
3
- import type { ModulePublicRuntimeConfig } from '../../module.js';
3
+ import type { ModuleOptions } from '../types.js';
4
4
  export { toMarkdownPath } from '../markdown-path.js';
5
5
  export declare function negotiateRepresentation(event: H3Event): ContentNegotiationResult;
6
6
  export type MarkdownRequestMode = {
@@ -29,7 +29,7 @@ interface ConvertHtmlOptions {
29
29
  /** Extra fields to inject at the root of mdream's emitted YAML frontmatter */
30
30
  additionalFrontmatter?: Record<string, string>;
31
31
  }
32
- export declare function convertHtmlToMarkdown(html: string, url: string, mdreamOptions: ModulePublicRuntimeConfig['mdreamOptions'], opts?: ConvertHtmlOptions): Promise<{
32
+ export declare function convertHtmlToMarkdown(html: string, url: string, mdreamOptions: ModuleOptions['mdreamOptions'], opts?: ConvertHtmlOptions): Promise<{
33
33
  updatedAt?: string | undefined;
34
34
  markdown: string;
35
35
  title: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-ai-ready",
3
3
  "type": "module",
4
- "version": "1.7.0",
4
+ "version": "1.7.2",
5
5
  "description": "Best practice AI & LLM discoverability for Nuxt sites.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -48,7 +48,7 @@
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"
51
+ "better-sqlite3": "^11.0.0 || ^12.0.0 || ^13.0.0"
52
52
  },
53
53
  "peerDependenciesMeta": {
54
54
  "@libsql/client": {
@@ -120,7 +120,7 @@
120
120
  "scripts": {
121
121
  "lint": "eslint .",
122
122
  "lint:fix": "eslint . --fix",
123
- "build": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt-module-build build && pnpm run build:devtools",
123
+ "build": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt-module-build build && pnpm run build:devtools && pnpm run test:dist",
124
124
  "build:devtools": "node -e \"require('fs').cpSync('devtools','dist/devtools',{recursive:true})\"",
125
125
  "dev": "nuxt dev playground",
126
126
  "dev:minimal": "nuxt dev playground",
@@ -134,6 +134,7 @@
134
134
  "test:run": "pnpm run prepare:fixtures && vitest run && pnpm test:nuxt5",
135
135
  "test:unit": "vitest run --project=unit",
136
136
  "test:e2e": "pnpm run prepare:fixtures && vitest run --project=e2e",
137
+ "test:dist": "node scripts/check-dist-imports.mjs",
137
138
  "typecheck": "nuxt typecheck && tsc -p test/types/tsconfig.json",
138
139
  "test:attw": "attw --pack",
139
140
  "test:nuxt5": "pnpm pack --out test/fixtures/nuxt5/module.tgz && pnpm install --dir test/fixtures/nuxt5 --no-frozen-lockfile --force --update-checksums && pnpm --dir test/fixtures/nuxt5 test"