nuxt-ai-ready 0.3.6 → 0.3.8
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.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
import { mkdir, stat, open, access, appendFile } from 'node:fs/promises';
|
|
1
2
|
import { dirname, join } from 'node:path';
|
|
2
3
|
import { useLogger, useNuxt, defineNuxtModule, createResolver, addTypeTemplate, hasNuxtModule, addServerHandler, addPlugin, extendPages } from '@nuxt/kit';
|
|
3
4
|
import defu from 'defu';
|
|
4
5
|
import { useSiteConfig, installNuxtSiteConfig, withSiteUrl } from 'nuxt-site-config/kit';
|
|
5
6
|
import { relative } from 'pathe';
|
|
6
7
|
import { readPackageJSON } from 'pkg-types';
|
|
8
|
+
import * as p from '@clack/prompts';
|
|
9
|
+
import { $fetch } from 'ofetch';
|
|
10
|
+
import { isTest, isCI } from 'std-env';
|
|
7
11
|
import { createHash } from 'node:crypto';
|
|
8
12
|
import { mkdirSync, createWriteStream } from 'node:fs';
|
|
9
|
-
import { mkdir, stat, open } from 'node:fs/promises';
|
|
10
13
|
import { encodeLines } from '@toon-format/toon';
|
|
11
14
|
import { createLlmsTxtStream } from 'mdream/llms-txt';
|
|
12
15
|
import { createStorage } from 'unstorage';
|
|
@@ -14,6 +17,57 @@ import fsDriver from 'unstorage/drivers/fs';
|
|
|
14
17
|
|
|
15
18
|
const logger = useLogger("nuxt-ai-ready");
|
|
16
19
|
|
|
20
|
+
function hookNuxtSeoProLicense() {
|
|
21
|
+
const nuxt = useNuxt();
|
|
22
|
+
const isBuild = !nuxt.options.dev && !nuxt.options._prepare;
|
|
23
|
+
if (isBuild && !nuxt._isNuxtSeoProVerifying) {
|
|
24
|
+
const license = nuxt.options.runtimeConfig.seoProKey || process.env.NUXT_SEO_PRO_KEY;
|
|
25
|
+
if (isTest) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (!isCI && !license) {
|
|
29
|
+
p.log.warn("\u26A0\uFE0F Building without license in non-CI environment. A license is required for production builds.");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (!license) {
|
|
33
|
+
p.log.error("\u{1F510} Nuxt SEO Pro license required");
|
|
34
|
+
p.note("Set NUXT_SEO_PRO_KEY or configure via module options.\n\nhttps://nuxtseo.com/pro/dashboard", "Get your license");
|
|
35
|
+
throw new Error("Missing Nuxt SEO Pro license key.");
|
|
36
|
+
}
|
|
37
|
+
nuxt._isNuxtSeoProVerifying = true;
|
|
38
|
+
nuxt.hooks.hook("build:before", async () => {
|
|
39
|
+
p.intro("Nuxt SEO Pro: License Verification");
|
|
40
|
+
const siteConfig = useSiteConfig();
|
|
41
|
+
const spinner = p.spinner();
|
|
42
|
+
spinner.start("\u{1F511} Verifying Nuxt SEO Pro license...");
|
|
43
|
+
const siteUrl = siteConfig.url?.startsWith("http") ? siteConfig.url : void 0;
|
|
44
|
+
const siteName = siteConfig.name || void 0;
|
|
45
|
+
const res = await $fetch("https://nuxtseo.com/api/pro/verify", {
|
|
46
|
+
method: "POST",
|
|
47
|
+
body: { apiKey: license, siteUrl, siteName }
|
|
48
|
+
}).catch((err) => {
|
|
49
|
+
if (err?.response?.status === 401) {
|
|
50
|
+
spinner.stop("\u274C Invalid API key");
|
|
51
|
+
p.note("Your API key is invalid.\n\nhttps://nuxtseo.com/pro/dashboard", "License Issue");
|
|
52
|
+
throw new Error("Invalid Nuxt SEO Pro API key.");
|
|
53
|
+
}
|
|
54
|
+
if (err?.response?.status === 403) {
|
|
55
|
+
spinner.stop("\u274C No active subscription");
|
|
56
|
+
p.note("Your subscription has expired or is inactive.\n\nhttps://nuxtseo.com/pro/dashboard", "License Issue");
|
|
57
|
+
throw new Error("No active Nuxt SEO Pro subscription.");
|
|
58
|
+
}
|
|
59
|
+
logger.error(err);
|
|
60
|
+
return null;
|
|
61
|
+
});
|
|
62
|
+
if (!res) {
|
|
63
|
+
spinner.stop("\u26A0\uFE0F License verification skipped (network issue)");
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
spinner.stop("License verified \u2713");
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
17
71
|
function createContentHashManager(options) {
|
|
18
72
|
const { storagePath, debug = false } = options;
|
|
19
73
|
let storage;
|
|
@@ -405,6 +459,7 @@ const module$1 = defineNuxtModule({
|
|
|
405
459
|
return;
|
|
406
460
|
}
|
|
407
461
|
await installNuxtSiteConfig();
|
|
462
|
+
hookNuxtSeoProLicense();
|
|
408
463
|
nuxt.options.nitro.alias = nuxt.options.nitro.alias || {};
|
|
409
464
|
nuxt.options.alias["#ai-ready"] = resolve("./runtime");
|
|
410
465
|
if (!nuxt.options.mcp?.name) {
|
|
@@ -547,8 +602,23 @@ export {}
|
|
|
547
602
|
setupPrerenderHandler(mergedLlmsTxt, config.timestamps);
|
|
548
603
|
}
|
|
549
604
|
nuxt.options.nitro.routeRules = nuxt.options.nitro.routeRules || {};
|
|
605
|
+
nuxt.options.nitro.routeRules["/llms.txt"] = { headers: { "Content-Type": "text/plain; charset=utf-8" } };
|
|
606
|
+
nuxt.options.nitro.routeRules["/llms-full.txt"] = { headers: { "Content-Type": "text/plain; charset=utf-8" } };
|
|
550
607
|
nuxt.options.nitro.routeRules["/llms.toon"] = { headers: { "Content-Type": "text/toon; charset=utf-8" } };
|
|
551
608
|
nuxt.options.nitro.routeRules["/llms-full.toon"] = { headers: { "Content-Type": "text/toon; charset=utf-8" } };
|
|
609
|
+
nuxt.hooks.hook("nitro:build:before", (nitro) => {
|
|
610
|
+
nitro.hooks.hook("compiled", async () => {
|
|
611
|
+
const headersPath = join(nitro.options.output.publicDir, "_headers");
|
|
612
|
+
const exists = await access(headersPath).then(() => true).catch(() => false);
|
|
613
|
+
if (exists) {
|
|
614
|
+
await appendFile(headersPath, `
|
|
615
|
+
/*.md
|
|
616
|
+
Content-Type: text/markdown; charset=utf-8
|
|
617
|
+
`);
|
|
618
|
+
logger.debug("Appended .md charset header to _headers");
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
});
|
|
552
622
|
}
|
|
553
623
|
});
|
|
554
624
|
|
|
@@ -3,7 +3,7 @@ import { useEvent } from "nitropack/runtime";
|
|
|
3
3
|
export default {
|
|
4
4
|
uri: "resource://nuxt-ai-ready/pages",
|
|
5
5
|
name: "All Pages",
|
|
6
|
-
description: "Page-level metadata (route, title, description,
|
|
6
|
+
description: "Page-level metadata (route, title, description, headings, chunkIds, updatedAt) in TOON format. Each page includes chunkIds to join with pages-chunks resource for chunk-level content. TOON is token-efficient JSON encoding (see https://toonformat.dev)",
|
|
7
7
|
metadata: {
|
|
8
8
|
mimeType: "text/toon"
|
|
9
9
|
},
|
|
@@ -3,14 +3,6 @@ import { extractionPlugin } from "mdream/plugins";
|
|
|
3
3
|
import { withMinimalPreset } from "mdream/preset/minimal";
|
|
4
4
|
import { htmlToMarkdownSplitChunks } from "mdream/splitter";
|
|
5
5
|
import { estimateTokenCount } from "tokenx";
|
|
6
|
-
function stripFrontmatter(text) {
|
|
7
|
-
if (!text.startsWith("---\n"))
|
|
8
|
-
return text;
|
|
9
|
-
const endIdx = text.indexOf("\n---", 4);
|
|
10
|
-
if (endIdx === -1)
|
|
11
|
-
return text;
|
|
12
|
-
return text.slice(endIdx + 4).trimStart();
|
|
13
|
-
}
|
|
14
6
|
function normalizeWhitespace(text) {
|
|
15
7
|
return text.replace(/\u00A0/g, " ");
|
|
16
8
|
}
|
|
@@ -42,7 +34,7 @@ export function convertHtmlToMarkdownChunks(html, url, mdreamOptions) {
|
|
|
42
34
|
options.plugins = [extractPlugin, ...options.plugins || []];
|
|
43
35
|
}
|
|
44
36
|
const rawMarkdown = htmlToMarkdown(html, options);
|
|
45
|
-
const markdown = normalizeWhitespace(
|
|
37
|
+
const markdown = normalizeWhitespace(rawMarkdown);
|
|
46
38
|
const rawChunks = htmlToMarkdownSplitChunks(html, {
|
|
47
39
|
...options,
|
|
48
40
|
headersToSplitOn: [TagIdMap.h1, TagIdMap.h2, TagIdMap.h3],
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-ai-ready",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.8",
|
|
5
5
|
"description": "Best practice AI & LLM discoverability for Nuxt sites.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -32,22 +32,24 @@
|
|
|
32
32
|
"dist"
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@
|
|
35
|
+
"@clack/prompts": "^0.11.0",
|
|
36
|
+
"@nuxt/kit": "4.2.2",
|
|
36
37
|
"@toon-format/toon": "^2.1.0",
|
|
37
38
|
"consola": "^3.4.2",
|
|
38
39
|
"defu": "^6.1.4",
|
|
39
40
|
"mdream": "^0.15.3",
|
|
40
41
|
"minimatch": "^10.1.1",
|
|
41
|
-
"nuxt-site-config": "
|
|
42
|
+
"nuxt-site-config": "3.2.11",
|
|
43
|
+
"ofetch": "^1.5.1",
|
|
42
44
|
"pathe": "^2.0.3",
|
|
43
45
|
"pkg-types": "^2.3.0",
|
|
44
|
-
"std-env": "
|
|
46
|
+
"std-env": "3.10.0",
|
|
45
47
|
"tokenx": "^1.2.1",
|
|
46
48
|
"ufo": "^1.6.1",
|
|
47
49
|
"unstorage": "^1.17.3"
|
|
48
50
|
},
|
|
49
51
|
"devDependencies": {
|
|
50
|
-
"@antfu/eslint-config": "^6.
|
|
52
|
+
"@antfu/eslint-config": "^6.6.1",
|
|
51
53
|
"@arethetypeswrong/cli": "^0.18.2",
|
|
52
54
|
"@headlessui/vue": "^1.7.23",
|
|
53
55
|
"@nuxt/content": "^3.9.0",
|
|
@@ -67,8 +69,8 @@
|
|
|
67
69
|
"eslint": "^9.39.1",
|
|
68
70
|
"execa": "^9.6.1",
|
|
69
71
|
"happy-dom": "^20.0.11",
|
|
70
|
-
"nuxt": "^4.2.
|
|
71
|
-
"nuxt-site-config": "
|
|
72
|
+
"nuxt": "^4.2.2",
|
|
73
|
+
"nuxt-site-config": "3.2.11",
|
|
72
74
|
"playwright": "^1.57.0",
|
|
73
75
|
"playwright-core": "^1.57.0",
|
|
74
76
|
"postgres": "^3.4.7",
|
|
@@ -76,7 +78,7 @@
|
|
|
76
78
|
"vitest": "^4.0.15",
|
|
77
79
|
"vue": "^3.5.25",
|
|
78
80
|
"vue-router": "^4.6.3",
|
|
79
|
-
"vue-tsc": "^3.1.
|
|
81
|
+
"vue-tsc": "^3.1.8",
|
|
80
82
|
"zod": "^4.1.13"
|
|
81
83
|
},
|
|
82
84
|
"resolutions": {
|