nuxt-ai-ready 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +163 -9
- package/dist/runtime/server/plugins/sitemap-lastmod.d.ts +2 -0
- package/dist/runtime/server/plugins/sitemap-lastmod.js +22 -0
- package/dist/runtime/server/utils.d.ts +2 -1
- package/dist/runtime/server/utils.js +22 -9
- package/dist/runtime/types.d.ts +17 -0
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
|
5
5
|
[![Nuxt][nuxt-src]][nuxt-href]
|
|
6
6
|
|
|
7
|
-
> Best practice AI & LLM discoverability for Nuxt sites
|
|
7
|
+
> Best practice AI & LLM discoverability for Nuxt sites
|
|
8
8
|
|
|
9
9
|
## Why Nuxt AI Ready?
|
|
10
10
|
|
|
@@ -27,6 +27,7 @@ Nuxt AI Ready converts your indexable pages into clean markdown that AI systems
|
|
|
27
27
|
- 🌐 **Indexable Pages**: Integrating with [Nuxt Sitemap](https://nuxtseo.com/sitemap) to index only AI-allowed pages
|
|
28
28
|
- 📦 **Bulk Chunk Export**: Exported token optimized chunks ready for RAG and semantic search
|
|
29
29
|
- ⚡ **MCP Integration**: Let AI agents query your site directly
|
|
30
|
+
- ⏱️ **Automatic Last Updated**: Content freshness automatically tracked and added to your [sitemap.xml](https://nuxtseo.com/docs/ai-ready/guides/automatic-updated-at)
|
|
30
31
|
|
|
31
32
|
## Installation
|
|
32
33
|
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { dirname, join } from 'node:path';
|
|
1
2
|
import { useLogger, useNuxt, defineNuxtModule, createResolver, addTypeTemplate, hasNuxtModule, addServerHandler, addPlugin } from '@nuxt/kit';
|
|
2
3
|
import defu from 'defu';
|
|
3
4
|
import { useSiteConfig, installNuxtSiteConfig, withSiteUrl } from 'nuxt-site-config/kit';
|
|
@@ -5,13 +6,113 @@ import { relative } from 'pathe';
|
|
|
5
6
|
import { readPackageJSON } from 'pkg-types';
|
|
6
7
|
import { createHash } from 'node:crypto';
|
|
7
8
|
import { mkdirSync, createWriteStream } from 'node:fs';
|
|
8
|
-
import { stat, open } from 'node:fs/promises';
|
|
9
|
-
import { join, dirname } from 'node:path';
|
|
9
|
+
import { mkdir, stat, open } from 'node:fs/promises';
|
|
10
10
|
import { encodeLines } from '@toon-format/toon';
|
|
11
11
|
import { createLlmsTxtStream } from 'mdream/llms-txt';
|
|
12
|
+
import { createStorage } from 'unstorage';
|
|
13
|
+
import fsDriver from 'unstorage/drivers/fs';
|
|
12
14
|
|
|
13
15
|
const logger = useLogger("nuxt-ai-ready");
|
|
14
16
|
|
|
17
|
+
function createContentHashManager(options) {
|
|
18
|
+
const { storagePath, debug = false } = options;
|
|
19
|
+
let storage;
|
|
20
|
+
let manifest = {
|
|
21
|
+
pages: {},
|
|
22
|
+
version: "1"
|
|
23
|
+
};
|
|
24
|
+
async function initStorage() {
|
|
25
|
+
await mkdir(dirname(storagePath), { recursive: true });
|
|
26
|
+
storage = createStorage({
|
|
27
|
+
driver: fsDriver({ base: dirname(storagePath) })
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function hashContent(markdown) {
|
|
31
|
+
return createHash("sha256").update(markdown).digest("hex");
|
|
32
|
+
}
|
|
33
|
+
async function getManifest() {
|
|
34
|
+
if (!storage) {
|
|
35
|
+
await initStorage();
|
|
36
|
+
}
|
|
37
|
+
const stored = await storage.getItem("content-hashes.json");
|
|
38
|
+
if (stored) {
|
|
39
|
+
manifest = stored;
|
|
40
|
+
if (debug) {
|
|
41
|
+
logger.debug(`Loaded manifest with ${Object.keys(manifest.pages).length} pages`);
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
if (debug) {
|
|
45
|
+
logger.debug("No existing manifest found, starting fresh");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return manifest;
|
|
49
|
+
}
|
|
50
|
+
async function saveManifest() {
|
|
51
|
+
if (!storage) {
|
|
52
|
+
await initStorage();
|
|
53
|
+
}
|
|
54
|
+
await storage.setItem("content-hashes.json", manifest);
|
|
55
|
+
if (debug) {
|
|
56
|
+
logger.debug(`Saved manifest with ${Object.keys(manifest.pages).length} pages`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function updatePageHash(route, markdown, previousManifest) {
|
|
60
|
+
const contentHash = hashContent(markdown);
|
|
61
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
62
|
+
const existing = previousManifest.pages[route];
|
|
63
|
+
let result;
|
|
64
|
+
if (!existing) {
|
|
65
|
+
result = {
|
|
66
|
+
contentHash,
|
|
67
|
+
updatedAt: now,
|
|
68
|
+
firstSeenAt: now
|
|
69
|
+
};
|
|
70
|
+
if (debug) {
|
|
71
|
+
logger.debug(`New page detected: ${route}`);
|
|
72
|
+
}
|
|
73
|
+
} else if (existing.contentHash !== contentHash) {
|
|
74
|
+
result = {
|
|
75
|
+
contentHash,
|
|
76
|
+
updatedAt: now,
|
|
77
|
+
firstSeenAt: existing.firstSeenAt
|
|
78
|
+
};
|
|
79
|
+
if (debug) {
|
|
80
|
+
logger.debug(`Content changed: ${route}`);
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
result = {
|
|
84
|
+
contentHash: existing.contentHash,
|
|
85
|
+
updatedAt: existing.updatedAt,
|
|
86
|
+
firstSeenAt: existing.firstSeenAt
|
|
87
|
+
};
|
|
88
|
+
if (debug) {
|
|
89
|
+
logger.debug(`Content unchanged: ${route}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
manifest.pages[route] = result;
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
function setPageTimestamp(route, markdown, timestamp, previousManifest) {
|
|
96
|
+
const contentHash = hashContent(markdown);
|
|
97
|
+
const existing = previousManifest.pages[route];
|
|
98
|
+
manifest.pages[route] = {
|
|
99
|
+
contentHash,
|
|
100
|
+
updatedAt: timestamp,
|
|
101
|
+
firstSeenAt: existing?.firstSeenAt || timestamp
|
|
102
|
+
};
|
|
103
|
+
if (debug) {
|
|
104
|
+
logger.debug(`Manual timestamp set for ${route}: ${timestamp}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
getManifest,
|
|
109
|
+
saveManifest,
|
|
110
|
+
hashContent,
|
|
111
|
+
updatePageHash,
|
|
112
|
+
setPageTimestamp
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
15
116
|
function generateVectorId(route, chunkIdx) {
|
|
16
117
|
const hash = createHash("sha256").update(route).digest("hex").substring(0, 8);
|
|
17
118
|
return `${hash}-${chunkIdx}`;
|
|
@@ -30,7 +131,7 @@ async function updateFirstLine(filePath, newFirstLine) {
|
|
|
30
131
|
await fh.close();
|
|
31
132
|
}
|
|
32
133
|
}
|
|
33
|
-
function setupPrerenderHandler(llmsTxtConfig) {
|
|
134
|
+
function setupPrerenderHandler(llmsTxtConfig, timestampsConfig) {
|
|
34
135
|
const nuxt = useNuxt();
|
|
35
136
|
nuxt.hooks.hook("nitro:init", async (nitro) => {
|
|
36
137
|
let writer = null;
|
|
@@ -41,6 +142,18 @@ function setupPrerenderHandler(llmsTxtConfig) {
|
|
|
41
142
|
const startTime = Date.now();
|
|
42
143
|
const pagesChunksPath = join(nitro.options.output.publicDir, "llms-full.toon");
|
|
43
144
|
const pagesPath = join(nitro.options.output.publicDir, "llms.toon");
|
|
145
|
+
let contentHashManager = null;
|
|
146
|
+
let previousManifest = null;
|
|
147
|
+
if (timestampsConfig?.enabled) {
|
|
148
|
+
const manifestPath = join(
|
|
149
|
+
nuxt.options.rootDir,
|
|
150
|
+
timestampsConfig.manifestPath || "node_modules/.cache/nuxt-seo/ai-index/content-hashes.json"
|
|
151
|
+
);
|
|
152
|
+
contentHashManager = createContentHashManager({
|
|
153
|
+
storagePath: manifestPath,
|
|
154
|
+
debug: !!nuxt.options.debug
|
|
155
|
+
});
|
|
156
|
+
}
|
|
44
157
|
nitro.hooks.hook("prerender:generate", async (route) => {
|
|
45
158
|
if (!route.fileName?.endsWith(".md")) {
|
|
46
159
|
return;
|
|
@@ -61,15 +174,39 @@ function setupPrerenderHandler(llmsTxtConfig) {
|
|
|
61
174
|
notes: llmsTxtConfig.notes
|
|
62
175
|
});
|
|
63
176
|
writer = stream.getWriter();
|
|
177
|
+
if (contentHashManager && !previousManifest) {
|
|
178
|
+
previousManifest = await contentHashManager.getManifest();
|
|
179
|
+
}
|
|
64
180
|
mkdirSync(dirname(pagesChunksPath), { recursive: true });
|
|
65
181
|
mkdirSync(dirname(pagesPath), { recursive: true });
|
|
66
182
|
chunksStream = createWriteStream(pagesChunksPath, { encoding: "utf-8" });
|
|
67
183
|
chunksStream.write("pageChunks[999999]{id,route,content}:\n");
|
|
68
184
|
pagesStream = createWriteStream(pagesPath, { encoding: "utf-8" });
|
|
69
|
-
pagesStream.write("pages[999999]{route,title,description,headings,chunkIds}:\n");
|
|
185
|
+
pagesStream.write("pages[999999]{route,title,description,headings,chunkIds,updatedAt}:\n");
|
|
70
186
|
}
|
|
71
|
-
const { chunks, title, description, headings } = JSON.parse(route.contents || "{}");
|
|
187
|
+
const { chunks, title, description, headings, updatedAt: metaUpdatedAt } = JSON.parse(route.contents || "{}");
|
|
72
188
|
const markdown = chunks.map((c) => c.content).join("\n\n");
|
|
189
|
+
let pageTimestamp = {
|
|
190
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
191
|
+
};
|
|
192
|
+
let usedMetaTimestamp = false;
|
|
193
|
+
if (metaUpdatedAt) {
|
|
194
|
+
const parsedDate = new Date(metaUpdatedAt);
|
|
195
|
+
if (!Number.isNaN(parsedDate.getTime())) {
|
|
196
|
+
pageTimestamp.updatedAt = parsedDate.toISOString();
|
|
197
|
+
usedMetaTimestamp = true;
|
|
198
|
+
if (contentHashManager && previousManifest) {
|
|
199
|
+
contentHashManager.setPageTimestamp(pageRoute, markdown, pageTimestamp.updatedAt, previousManifest);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (!usedMetaTimestamp && contentHashManager && previousManifest) {
|
|
204
|
+
pageTimestamp = contentHashManager.updatePageHash(
|
|
205
|
+
pageRoute,
|
|
206
|
+
markdown,
|
|
207
|
+
previousManifest
|
|
208
|
+
);
|
|
209
|
+
}
|
|
73
210
|
await writer.write({
|
|
74
211
|
url: pageRoute,
|
|
75
212
|
title,
|
|
@@ -121,7 +258,8 @@ function setupPrerenderHandler(llmsTxtConfig) {
|
|
|
121
258
|
([tag, texts]) => texts.map((text) => `${tag}:${text}`)
|
|
122
259
|
).join("|") : "",
|
|
123
260
|
// Join chunkIds array to comma-separated string
|
|
124
|
-
chunkIds: chunkIds.join(",")
|
|
261
|
+
chunkIds: chunkIds.join(","),
|
|
262
|
+
updatedAt: pageTimestamp.updatedAt
|
|
125
263
|
};
|
|
126
264
|
if (pagesStream) {
|
|
127
265
|
const lines = Array.from(encodeLines({ pages: [pageDoc] }));
|
|
@@ -152,7 +290,11 @@ function setupPrerenderHandler(llmsTxtConfig) {
|
|
|
152
290
|
});
|
|
153
291
|
}
|
|
154
292
|
await updateFirstLine(pagesChunksPath, `pageChunks[${chunksProcessed}]{id,route,content}:`);
|
|
155
|
-
await updateFirstLine(pagesPath, `pages[${pageCount}]{route,title,description,headings,chunkIds}:`);
|
|
293
|
+
await updateFirstLine(pagesPath, `pages[${pageCount}]{route,title,description,headings,chunkIds,updatedAt}:`);
|
|
294
|
+
if (contentHashManager) {
|
|
295
|
+
await contentHashManager.saveManifest();
|
|
296
|
+
logger.debug("Saved content hash manifest");
|
|
297
|
+
}
|
|
156
298
|
logger.info(`Wrote llms-full.toon with ${chunksProcessed} chunks`);
|
|
157
299
|
logger.info(`Wrote llms.toon with ${pageCount} pages`);
|
|
158
300
|
const llmsTxtPath = join(nitro.options.output.publicDir, "llms.txt");
|
|
@@ -227,6 +369,10 @@ const module$1 = defineNuxtModule({
|
|
|
227
369
|
maxAge: 3600,
|
|
228
370
|
// 1 hour
|
|
229
371
|
swr: true
|
|
372
|
+
},
|
|
373
|
+
timestamps: {
|
|
374
|
+
enabled: false,
|
|
375
|
+
manifestPath: "node_modules/.cache/nuxt-seo/ai-index/content-hashes.json"
|
|
230
376
|
}
|
|
231
377
|
};
|
|
232
378
|
},
|
|
@@ -350,6 +496,7 @@ export {}
|
|
|
350
496
|
await nuxt.callHook("ai-ready:llms-txt", llmsTxtPayload);
|
|
351
497
|
mergedLlmsTxt.sections = llmsTxtPayload.sections;
|
|
352
498
|
mergedLlmsTxt.notes = llmsTxtPayload.notes.length > 0 ? llmsTxtPayload.notes : void 0;
|
|
499
|
+
const timestampsManifestPath = config.timestamps?.enabled ? join(nuxt.options.rootDir, config.timestamps.manifestPath || "node_modules/.cache/nuxt-seo/ai-index/content-hashes.json") : void 0;
|
|
353
500
|
nuxt.options.runtimeConfig["nuxt-ai-ready"] = {
|
|
354
501
|
version: version || "0.0.0",
|
|
355
502
|
debug: config.debug || false,
|
|
@@ -358,8 +505,15 @@ export {}
|
|
|
358
505
|
maxAge: 3600,
|
|
359
506
|
swr: true
|
|
360
507
|
}),
|
|
361
|
-
llmsTxt: mergedLlmsTxt
|
|
508
|
+
llmsTxt: mergedLlmsTxt,
|
|
509
|
+
timestampsManifestPath
|
|
362
510
|
};
|
|
511
|
+
if (config.timestamps?.enabled && hasNuxtModule("@nuxtjs/sitemap")) {
|
|
512
|
+
nuxt.hook("nitro:config", (nitroConfig) => {
|
|
513
|
+
nitroConfig.plugins = nitroConfig.plugins || [];
|
|
514
|
+
nitroConfig.plugins.push(resolve("./runtime/server/plugins/sitemap-lastmod"));
|
|
515
|
+
});
|
|
516
|
+
}
|
|
363
517
|
addServerHandler({
|
|
364
518
|
middleware: true,
|
|
365
519
|
handler: resolve("./runtime/server/middleware/mdream")
|
|
@@ -371,7 +525,7 @@ export {}
|
|
|
371
525
|
addServerHandler({ route: "/llms-full.txt", handler: resolve("./runtime/server/routes/llms.txt.get") });
|
|
372
526
|
const isStatic = nuxt.options.nitro.static || nuxt.options._generate || false;
|
|
373
527
|
if (isStatic || nuxt.options.nitro.prerender?.routes?.length) {
|
|
374
|
-
setupPrerenderHandler(mergedLlmsTxt);
|
|
528
|
+
setupPrerenderHandler(mergedLlmsTxt, config.timestamps);
|
|
375
529
|
}
|
|
376
530
|
nuxt.options.nitro.routeRules = nuxt.options.nitro.routeRules || {};
|
|
377
531
|
nuxt.options.nitro.routeRules["/llms.toon"] = { headers: { "Content-Type": "text/toon; charset=utf-8" } };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { defineNitroPlugin, useRuntimeConfig } from "nitropack/runtime";
|
|
3
|
+
export default defineNitroPlugin((nitroApp) => {
|
|
4
|
+
const config = useRuntimeConfig();
|
|
5
|
+
const manifestPath = config["nuxt-ai-ready"]?.timestampsManifestPath;
|
|
6
|
+
if (!manifestPath) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
nitroApp.hooks.hook("sitemap:resolved", async (ctx) => {
|
|
10
|
+
const manifest = await readFile(manifestPath, "utf-8").then((data) => JSON.parse(data)).catch(() => null);
|
|
11
|
+
if (!manifest) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
for (const url of ctx.urls) {
|
|
15
|
+
const route = url.loc.replace(/^https?:\/\/[^/]+/, "").replace(/\/$/, "") || "/";
|
|
16
|
+
const pageData = manifest.pages[route];
|
|
17
|
+
if (pageData?.updatedAt) {
|
|
18
|
+
url.lastmod = pageData.updatedAt;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { ModulePublicRuntimeConfig } from '../../module.js';
|
|
2
2
|
export declare function convertHtmlToMarkdownChunks(html: string, url: string, mdreamOptions: ModulePublicRuntimeConfig['mdreamOptions']): Promise<{
|
|
3
|
+
headings: Record<string, string[]>;
|
|
4
|
+
updatedAt?: string | undefined;
|
|
3
5
|
chunks: import("mdream").MarkdownChunk[];
|
|
4
6
|
title: string;
|
|
5
7
|
description: string;
|
|
6
|
-
headings: Record<string, string[]>;
|
|
7
8
|
}>;
|
|
@@ -6,12 +6,19 @@ import { estimateTokenCount } from "tokenx";
|
|
|
6
6
|
export async function convertHtmlToMarkdownChunks(html, url, mdreamOptions) {
|
|
7
7
|
let title = "";
|
|
8
8
|
let description = "";
|
|
9
|
+
let updatedAt;
|
|
9
10
|
const extractPlugin = extractionPlugin({
|
|
10
11
|
title(el) {
|
|
11
12
|
title = el.textContent;
|
|
12
13
|
},
|
|
13
14
|
'meta[name="description"]': (el) => {
|
|
14
15
|
description = el.attributes.content || "";
|
|
16
|
+
},
|
|
17
|
+
// Extract timestamp from various meta tag formats
|
|
18
|
+
'meta[property="article:modified_time"], meta[name="last-modified"], meta[name="updated"], meta[property="og:updated_time"], meta[name="lastmod"]': (el) => {
|
|
19
|
+
if (!updatedAt && el.attributes.content) {
|
|
20
|
+
updatedAt = el.attributes.content;
|
|
21
|
+
}
|
|
15
22
|
}
|
|
16
23
|
});
|
|
17
24
|
let options = {
|
|
@@ -38,13 +45,19 @@ export async function convertHtmlToMarkdownChunks(html, url, mdreamOptions) {
|
|
|
38
45
|
for await (const chunk of chunksStream) {
|
|
39
46
|
chunks.push(chunk);
|
|
40
47
|
}
|
|
41
|
-
return {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
return {
|
|
49
|
+
chunks,
|
|
50
|
+
title,
|
|
51
|
+
description,
|
|
52
|
+
...updatedAt && { updatedAt },
|
|
53
|
+
headings: chunks.reduce((set, m) => {
|
|
54
|
+
Object.entries(m.metadata?.headers || {}).forEach(([k, v]) => {
|
|
55
|
+
if (!set[k])
|
|
56
|
+
set[k] = [];
|
|
57
|
+
if (v && !set[k].includes(v))
|
|
58
|
+
set[k].push(v);
|
|
59
|
+
});
|
|
60
|
+
return set;
|
|
61
|
+
}, {})
|
|
62
|
+
};
|
|
50
63
|
}
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -82,6 +82,21 @@ export interface ModuleOptions {
|
|
|
82
82
|
pagesChunks?: boolean;
|
|
83
83
|
};
|
|
84
84
|
};
|
|
85
|
+
/**
|
|
86
|
+
* Content timestamp tracking configuration
|
|
87
|
+
*/
|
|
88
|
+
timestamps?: {
|
|
89
|
+
/**
|
|
90
|
+
* Enable timestamp tracking
|
|
91
|
+
* @default false
|
|
92
|
+
*/
|
|
93
|
+
enabled?: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Path to store content hash manifest
|
|
96
|
+
* @default 'node_modules/.cache/nuxt-seo/ai-index/content-hashes.json'
|
|
97
|
+
*/
|
|
98
|
+
manifestPath?: string;
|
|
99
|
+
};
|
|
85
100
|
}
|
|
86
101
|
/**
|
|
87
102
|
* Individual chunk entry in llms-full.toon (one per chunk)
|
|
@@ -112,6 +127,8 @@ export interface BulkDocument {
|
|
|
112
127
|
headings: Array<Record<string, string>>;
|
|
113
128
|
/** All chunk IDs for this page (first ID can be used as document ID) */
|
|
114
129
|
chunkIds: string[];
|
|
130
|
+
/** ISO 8601 timestamp of last content update */
|
|
131
|
+
updatedAt?: string;
|
|
115
132
|
}
|
|
116
133
|
/**
|
|
117
134
|
* Hook context for markdown processing (Nitro runtime hook)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-ai-ready",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"description": "Best practice AI & LLM discoverability for Nuxt sites.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -33,20 +33,21 @@
|
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@nuxt/kit": "4.2.1",
|
|
36
|
-
"@toon-format/toon": "^2.0
|
|
36
|
+
"@toon-format/toon": "^2.1.0",
|
|
37
37
|
"consola": "^3.4.2",
|
|
38
38
|
"defu": "^6.1.4",
|
|
39
|
-
"mdream": "^0.15.
|
|
39
|
+
"mdream": "^0.15.2",
|
|
40
40
|
"minimatch": "^10.1.1",
|
|
41
41
|
"nuxt-site-config": "^3.2.11",
|
|
42
42
|
"pathe": "^2.0.3",
|
|
43
43
|
"pkg-types": "^2.3.0",
|
|
44
44
|
"std-env": "^3.10.0",
|
|
45
45
|
"tokenx": "^1.2.1",
|
|
46
|
-
"ufo": "^1.6.1"
|
|
46
|
+
"ufo": "^1.6.1",
|
|
47
|
+
"unstorage": "^1.17.3"
|
|
47
48
|
},
|
|
48
49
|
"devDependencies": {
|
|
49
|
-
"@antfu/eslint-config": "^6.4.
|
|
50
|
+
"@antfu/eslint-config": "^6.4.2",
|
|
50
51
|
"@arethetypeswrong/cli": "^0.18.2",
|
|
51
52
|
"@headlessui/vue": "^1.7.23",
|
|
52
53
|
"@nuxt/content": "^3.9.0",
|
|
@@ -57,7 +58,7 @@
|
|
|
57
58
|
"@nuxtjs/eslint-config-typescript": "^12.1.0",
|
|
58
59
|
"@nuxtjs/i18n": "^10.2.1",
|
|
59
60
|
"@nuxtjs/mcp-toolkit": "^0.5.1",
|
|
60
|
-
"@nuxtjs/robots": "^5.6.
|
|
61
|
+
"@nuxtjs/robots": "^5.6.1",
|
|
61
62
|
"@nuxtjs/sitemap": "^7.4.8",
|
|
62
63
|
"@vitest/coverage-v8": "^4.0.15",
|
|
63
64
|
"@vueuse/nuxt": "^14.1.0",
|
|
@@ -75,7 +76,7 @@
|
|
|
75
76
|
"vitest": "^4.0.15",
|
|
76
77
|
"vue": "^3.5.25",
|
|
77
78
|
"vue-router": "^4.6.3",
|
|
78
|
-
"vue-tsc": "^3.1.
|
|
79
|
+
"vue-tsc": "^3.1.6",
|
|
79
80
|
"zod": "^4.1.13"
|
|
80
81
|
},
|
|
81
82
|
"resolutions": {
|