nuxt-ai-ready 0.11.0 → 0.12.1
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 +8 -2
- package/dist/cli.mjs +11 -8
- package/dist/module.d.mts +7 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +86 -77
- package/dist/runtime/index.d.ts +3 -3
- package/dist/runtime/index.js +2 -1
- package/dist/runtime/server/db/drizzle/client.d.ts +20 -0
- package/dist/runtime/server/db/drizzle/client.js +29 -0
- package/dist/runtime/server/db/drizzle/index.d.ts +10 -0
- package/dist/runtime/server/db/drizzle/index.js +33 -0
- package/dist/runtime/server/db/drizzle/providers/bun.d.ts +7 -0
- package/dist/runtime/server/db/drizzle/providers/bun.js +25 -0
- package/dist/runtime/server/db/drizzle/providers/d1.d.ts +7 -0
- package/dist/runtime/server/db/drizzle/providers/d1.js +19 -0
- package/dist/runtime/server/db/drizzle/providers/libsql.d.ts +7 -0
- package/dist/runtime/server/db/drizzle/providers/libsql.js +15 -0
- package/dist/runtime/server/db/drizzle/providers/neon.d.ts +7 -0
- package/dist/runtime/server/db/drizzle/providers/neon.js +18 -0
- package/dist/runtime/server/db/drizzle/providers/sqlite.d.ts +8 -0
- package/dist/runtime/server/db/drizzle/providers/sqlite.js +25 -0
- package/dist/runtime/server/db/drizzle/queries.d.ts +190 -0
- package/dist/runtime/server/db/drizzle/queries.js +575 -0
- package/dist/runtime/server/db/drizzle/raw.d.ts +28 -0
- package/dist/runtime/server/db/drizzle/raw.js +94 -0
- package/dist/runtime/server/db/index.d.ts +2 -12
- package/dist/runtime/server/db/index.js +1 -36
- package/dist/runtime/server/db/queries.d.ts +5 -0
- package/dist/runtime/server/db/queries.js +31 -5
- package/dist/runtime/server/db/schema/postgres.d.ts +1500 -0
- package/dist/runtime/server/db/schema/postgres.js +66 -0
- package/dist/runtime/server/db/schema/sqlite.d.ts +1580 -0
- package/dist/runtime/server/db/schema/sqlite.js +66 -0
- package/dist/runtime/server/db/schema-sql.d.ts +6 -2
- package/dist/runtime/server/db/schema-sql.js +73 -92
- package/dist/runtime/server/db/shared.d.ts +1 -6
- package/dist/runtime/server/db/shared.js +4 -21
- package/dist/runtime/server/plugins/db-lifecycle.js +3 -3
- package/dist/runtime/server/plugins/sitemap-seeder.js +19 -2
- package/dist/runtime/server/routes/__ai-ready/restore.post.js +2 -2
- package/dist/runtime/server/routes/__ai-ready-debug.get.js +4 -4
- package/dist/runtime/server/utils/checkStale.js +19 -31
- package/dist/runtime/server/utils/indexnow-shared.js +1 -1
- package/dist/runtime/server/utils/indexnow.js +12 -13
- package/dist/runtime/server/utils/keywords.js +6 -2
- package/dist/runtime/server/utils/llms-full.js +5 -2
- package/dist/runtime/server/utils.d.ts +1 -0
- package/dist/runtime/server/utils.js +8 -12
- package/dist/runtime/types.d.ts +10 -4
- package/package.json +42 -32
- package/dist/runtime/server/db/provider/d1.d.ts +0 -3
- package/dist/runtime/server/db/provider/d1.js +0 -7
- package/dist/runtime/server/db/provider/libsql.d.ts +0 -3
- package/dist/runtime/server/db/provider/libsql.js +0 -9
- package/dist/runtime/server/db/provider/sqlite-bun.d.ts +0 -3
- package/dist/runtime/server/db/provider/sqlite-bun.js +0 -13
- package/dist/runtime/server/db/provider/sqlite-node.d.ts +0 -3
- package/dist/runtime/server/db/provider/sqlite-node.js +0 -13
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
## Why Nuxt AI Ready?
|
|
10
10
|
|
|
11
|
-
ChatGPT search interest doubled in the past year. Users now ask AI assistants questions your site could answer
|
|
11
|
+
[ChatGPT](https://chatgpt.com) search interest doubled in the past year. Users now ask AI assistants questions your site could answer - but LLMs only cite sources they can parse.
|
|
12
12
|
|
|
13
13
|
Two standards are emerging: [llms.txt](https://llmstxt.org/) (4,400 searches/mo, +26,900% YoY) for AI-readable site summaries, and [MCP](https://modelcontextprotocol.io/) (22,200 searches/mo) for letting agents query your content directly.
|
|
14
14
|
|
|
@@ -33,6 +33,12 @@ Install `nuxt-ai-ready` dependency to your project:
|
|
|
33
33
|
npx nuxi@latest module add nuxt-ai-ready
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
> [!TIP]
|
|
37
|
+
> Generate an Agent Skill for this package using [skilld](https://github.com/harlan-zw/skilld):
|
|
38
|
+
> ```bash
|
|
39
|
+
> npx skilld add nuxt-ai-ready
|
|
40
|
+
> ```
|
|
41
|
+
|
|
36
42
|
## Documentation
|
|
37
43
|
|
|
38
44
|
[📖 Read the full documentation](https://nuxtseo.com/ai-ready) for more information.
|
|
@@ -41,7 +47,7 @@ npx nuxi@latest module add nuxt-ai-ready
|
|
|
41
47
|
|
|
42
48
|
<p align="center">
|
|
43
49
|
<a href="https://raw.githubusercontent.com/harlan-zw/static/main/sponsors.svg">
|
|
44
|
-
<img src='https://raw.githubusercontent.com/harlan-zw/static/main/sponsors.svg'/>
|
|
50
|
+
<img src='https://raw.githubusercontent.com/harlan-zw/static/main/sponsors.svg' alt="Sponsors"/>
|
|
45
51
|
</a>
|
|
46
52
|
</p>
|
|
47
53
|
|
package/dist/cli.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import { defineCommand, runMain } from 'citty';
|
|
|
5
5
|
import { consola } from 'consola';
|
|
6
6
|
import { colors } from 'consola/utils';
|
|
7
7
|
import { resolve, join } from 'pathe';
|
|
8
|
+
import { readPackageJSON } from 'pkg-types';
|
|
8
9
|
|
|
9
10
|
async function getSecret(cwd) {
|
|
10
11
|
const secretPath = join(cwd, "node_modules/.cache/nuxt/ai-ready/secret");
|
|
@@ -16,10 +17,11 @@ async function getSecret(cwd) {
|
|
|
16
17
|
const main = defineCommand({
|
|
17
18
|
meta: {
|
|
18
19
|
name: "nuxt-ai-ready",
|
|
19
|
-
description: "Nuxt AI Ready CLI"
|
|
20
|
+
description: "Nuxt AI Ready CLI",
|
|
21
|
+
version: await readPackageJSON(import.meta.url).then((p) => p.version || "0.0.0")
|
|
20
22
|
},
|
|
21
23
|
subCommands: {
|
|
22
|
-
status: defineCommand({
|
|
24
|
+
status: () => defineCommand({
|
|
23
25
|
meta: {
|
|
24
26
|
name: "status",
|
|
25
27
|
description: "Show indexing status and IndexNow sync progress"
|
|
@@ -71,7 +73,7 @@ const main = defineCommand({
|
|
|
71
73
|
}
|
|
72
74
|
}
|
|
73
75
|
}),
|
|
74
|
-
poll: defineCommand({
|
|
76
|
+
poll: () => defineCommand({
|
|
75
77
|
meta: {
|
|
76
78
|
name: "poll",
|
|
77
79
|
description: "Trigger page indexing"
|
|
@@ -131,7 +133,7 @@ const main = defineCommand({
|
|
|
131
133
|
}
|
|
132
134
|
}
|
|
133
135
|
}),
|
|
134
|
-
restore: defineCommand({
|
|
136
|
+
restore: () => defineCommand({
|
|
135
137
|
meta: {
|
|
136
138
|
name: "restore",
|
|
137
139
|
description: "Restore database from prerendered dump"
|
|
@@ -145,8 +147,9 @@ const main = defineCommand({
|
|
|
145
147
|
},
|
|
146
148
|
clear: {
|
|
147
149
|
type: "boolean",
|
|
148
|
-
description: "Clear existing pages first
|
|
149
|
-
default: true
|
|
150
|
+
description: "Clear existing pages first",
|
|
151
|
+
default: true,
|
|
152
|
+
negativeDescription: "Restore without clearing existing pages"
|
|
150
153
|
},
|
|
151
154
|
cwd: {
|
|
152
155
|
type: "string",
|
|
@@ -179,7 +182,7 @@ const main = defineCommand({
|
|
|
179
182
|
}
|
|
180
183
|
}
|
|
181
184
|
}),
|
|
182
|
-
prune: defineCommand({
|
|
185
|
+
prune: () => defineCommand({
|
|
183
186
|
meta: {
|
|
184
187
|
name: "prune",
|
|
185
188
|
description: "Remove stale routes from database"
|
|
@@ -243,7 +246,7 @@ const main = defineCommand({
|
|
|
243
246
|
}
|
|
244
247
|
}
|
|
245
248
|
}),
|
|
246
|
-
indexnow: defineCommand({
|
|
249
|
+
indexnow: () => defineCommand({
|
|
247
250
|
meta: {
|
|
248
251
|
name: "indexnow",
|
|
249
252
|
description: "Trigger IndexNow sync"
|
package/dist/module.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
-
import {
|
|
2
|
+
import { LlmsTxtConfig, ModuleOptions } from '../dist/runtime/types.js';
|
|
3
3
|
export { ModuleOptions } from '../dist/runtime/types.js';
|
|
4
4
|
|
|
5
5
|
interface ParsedMarkdownResult {
|
|
@@ -27,6 +27,12 @@ interface ModuleHooks {
|
|
|
27
27
|
notes: string[];
|
|
28
28
|
}) => void | Promise<void>;
|
|
29
29
|
}
|
|
30
|
+
declare module '@nuxt/schema' {
|
|
31
|
+
interface NuxtHooks {
|
|
32
|
+
'ai-ready:page:markdown': ModuleHooks['ai-ready:page:markdown'];
|
|
33
|
+
'ai-ready:llms-txt': ModuleHooks['ai-ready:llms-txt'];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
30
36
|
interface ModulePublicRuntimeConfig {
|
|
31
37
|
debug: boolean;
|
|
32
38
|
debugCron: boolean;
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash, randomBytes } from 'node:crypto';
|
|
2
2
|
import { mkdir, writeFile, appendFile, stat, access } from 'node:fs/promises';
|
|
3
3
|
import { join, dirname } from 'node:path';
|
|
4
|
-
import { useLogger, useNuxt,
|
|
4
|
+
import { useLogger, useNuxt, addTypeTemplate, addTemplate, defineNuxtModule, createResolver, hasNuxtModule, addServerHandler, addPlugin, addServerPlugin } from '@nuxt/kit';
|
|
5
5
|
import defu from 'defu';
|
|
6
6
|
import { useSiteConfig, installNuxtSiteConfig, withSiteUrl } from 'nuxt-site-config/kit';
|
|
7
7
|
import { readPackageJSON } from 'pkg-types';
|
|
@@ -11,7 +11,7 @@ import { isTest, isCI } from 'std-env';
|
|
|
11
11
|
import { parseSitemapXml } from '@nuxtjs/sitemap/utils';
|
|
12
12
|
import { colorize } from 'consola/utils';
|
|
13
13
|
import { withBase } from 'ufo';
|
|
14
|
-
import {
|
|
14
|
+
import { initSchema, computeContentHash, insertPage, queryAllPages, exportDbDump } from '../dist/runtime/server/db/shared.js';
|
|
15
15
|
import { comparePageHashes, submitToIndexNowShared } from '../dist/runtime/server/utils/indexnow-shared.js';
|
|
16
16
|
import { buildLlmsFullTxtHeader, formatPageForLlmsFullTxt } from '../dist/runtime/server/utils/llms-full.js';
|
|
17
17
|
import { join as join$1, isAbsolute } from 'pathe';
|
|
@@ -59,12 +59,12 @@ function hookNuxtSeoProLicense() {
|
|
|
59
59
|
}
|
|
60
60
|
}).catch((err) => {
|
|
61
61
|
if (err?.response?.status === 401) {
|
|
62
|
-
spinner.
|
|
62
|
+
spinner.error("Invalid API key");
|
|
63
63
|
p.note("Your API key is invalid.\n\nhttps://nuxtseo.com/pro/dashboard", "License Issue");
|
|
64
64
|
throw new Error("Invalid Nuxt SEO Pro API key.");
|
|
65
65
|
}
|
|
66
66
|
if (err?.response?.status === 403) {
|
|
67
|
-
spinner.
|
|
67
|
+
spinner.error("No active subscription");
|
|
68
68
|
p.note("Your subscription has expired or is inactive.\n\nhttps://nuxtseo.com/pro/dashboard", "License Issue");
|
|
69
69
|
throw new Error("No active Nuxt SEO Pro subscription.");
|
|
70
70
|
}
|
|
@@ -72,7 +72,7 @@ function hookNuxtSeoProLicense() {
|
|
|
72
72
|
return null;
|
|
73
73
|
});
|
|
74
74
|
if (!res) {
|
|
75
|
-
spinner.
|
|
75
|
+
spinner.cancel("License verification skipped (network issue)");
|
|
76
76
|
return;
|
|
77
77
|
}
|
|
78
78
|
spinner.stop("License verified \u2713");
|
|
@@ -81,6 +81,9 @@ function hookNuxtSeoProLicense() {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
const BUILD_FETCH_TIMEOUT = 15e3;
|
|
84
|
+
const RE_HTML_MD_EXT = /\.(html|md)$/;
|
|
85
|
+
const RE_INDEX_SUFFIX = /\/index$/;
|
|
86
|
+
const RE_MD_EXT = /\.md$/;
|
|
84
87
|
async function fetchPreviousMeta(siteUrl, indexNow) {
|
|
85
88
|
const metaUrl = `${siteUrl}/__ai-ready/pages.meta.json`;
|
|
86
89
|
logger.info(`Fetching previous build meta from ${metaUrl}`);
|
|
@@ -140,13 +143,21 @@ async function initCrawler(state) {
|
|
|
140
143
|
if (state.dbPath) {
|
|
141
144
|
logger.debug(`Creating directory for SQLite: ${dirname(state.dbPath)}`);
|
|
142
145
|
await mkdir(dirname(state.dbPath), { recursive: true });
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
146
|
+
const { default: Database } = await import('better-sqlite3');
|
|
147
|
+
const sqlite = new Database(state.dbPath);
|
|
148
|
+
const db = {
|
|
149
|
+
all: async (sql, params = []) => sqlite.prepare(sql).all(...params),
|
|
150
|
+
first: async (sql, params = []) => sqlite.prepare(sql).get(...params),
|
|
151
|
+
exec: async (sql, params = []) => {
|
|
152
|
+
sqlite.prepare(sql).run(...params);
|
|
153
|
+
},
|
|
154
|
+
close: async () => {
|
|
155
|
+
sqlite.close();
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
state.db = db;
|
|
159
|
+
await initSchema(db);
|
|
160
|
+
logger.debug(`Crawler initialized with SQLite at ${state.dbPath}`);
|
|
150
161
|
}
|
|
151
162
|
if (state.llmsFullTxtPath) {
|
|
152
163
|
logger.debug(`Creating directory for llms-full.txt: ${dirname(state.llmsFullTxtPath)}`);
|
|
@@ -200,7 +211,7 @@ async function processSitemapEntry(state, nuxt, nitro, entry) {
|
|
|
200
211
|
if (state.prerenderedRoutes.has(route)) {
|
|
201
212
|
return { crawled: false, skipped: true };
|
|
202
213
|
}
|
|
203
|
-
const mdRoute = route
|
|
214
|
+
const mdRoute = route.endsWith("/") ? `${route}index.md` : `${route}.md`;
|
|
204
215
|
const mdUrl = withBase(mdRoute, nitro.options.baseURL);
|
|
205
216
|
logger.debug(`Fetching markdown for ${route} \u2192 ${mdUrl}`);
|
|
206
217
|
const res = await globalThis.$fetch(mdUrl, {
|
|
@@ -305,14 +316,14 @@ function setupPrerenderHandler(options, dbPath, siteInfo, llmsTxtConfig, indexNo
|
|
|
305
316
|
let initPromise = null;
|
|
306
317
|
nitro.hooks.hook("prerender:generate", async (route) => {
|
|
307
318
|
if (route.error) {
|
|
308
|
-
const pageRoute2 = route.route.replace(
|
|
319
|
+
const pageRoute2 = route.route.replace(RE_HTML_MD_EXT, "").replace(RE_INDEX_SUFFIX, "") || "/";
|
|
309
320
|
state.errorRoutes.add(pageRoute2);
|
|
310
321
|
logger.debug(`Detected error page: ${pageRoute2}`);
|
|
311
322
|
return;
|
|
312
323
|
}
|
|
313
324
|
if (!route.fileName?.endsWith(".md"))
|
|
314
325
|
return;
|
|
315
|
-
let pageRoute = route.route.replace(
|
|
326
|
+
let pageRoute = route.route.replace(RE_MD_EXT, "");
|
|
316
327
|
if (pageRoute === "/index")
|
|
317
328
|
pageRoute = "/";
|
|
318
329
|
const pageStartTime = Date.now();
|
|
@@ -430,7 +441,7 @@ function setupPrerenderHandler(options, dbPath, siteInfo, llmsTxtConfig, indexNo
|
|
|
430
441
|
await writeLlmsFiles();
|
|
431
442
|
state.prerenderedRoutes.clear();
|
|
432
443
|
if (state.db)
|
|
433
|
-
await state.db.close();
|
|
444
|
+
await state.db.close?.();
|
|
434
445
|
});
|
|
435
446
|
} else if (usePrerenderHook) {
|
|
436
447
|
nitro.hooks.hook("prerender:done", async () => {
|
|
@@ -444,28 +455,17 @@ function setupPrerenderHandler(options, dbPath, siteInfo, llmsTxtConfig, indexNo
|
|
|
444
455
|
await writeLlmsFiles();
|
|
445
456
|
state.prerenderedRoutes.clear();
|
|
446
457
|
if (state.db)
|
|
447
|
-
await state.db.close();
|
|
458
|
+
await state.db.close?.();
|
|
448
459
|
});
|
|
449
460
|
}
|
|
450
461
|
});
|
|
451
462
|
}
|
|
452
463
|
|
|
453
464
|
function registerTypeTemplates(_ctx) {
|
|
454
|
-
addTemplate({
|
|
455
|
-
filename: "types/ai-ready-adapter.d.ts",
|
|
456
|
-
getContents: () => `declare module '#ai-ready/adapter' {
|
|
457
|
-
import type { Connector } from 'db0'
|
|
458
|
-
const connector: (config: unknown) => Connector
|
|
459
|
-
export default connector
|
|
460
|
-
}
|
|
461
|
-
`
|
|
462
|
-
});
|
|
463
465
|
addTypeTemplate({
|
|
464
466
|
filename: "types/nuxt-ai-ready-augments.d.ts",
|
|
465
467
|
getContents: () => `// Generated by nuxt-ai-ready
|
|
466
|
-
/// <reference path="./ai-ready-adapter.d.ts" />
|
|
467
468
|
/// <reference path="./ai-ready-virtual.d.ts" />
|
|
468
|
-
/// <reference path="./ai-ready-db-provider.d.ts" />
|
|
469
469
|
import type { MarkdownContext, PageIndexedContext } from 'nuxt-ai-ready'
|
|
470
470
|
import type { HTMLToMarkdownOptions } from 'mdream'
|
|
471
471
|
|
|
@@ -505,42 +505,26 @@ declare module '#ai-ready-virtual/logger.mjs' {
|
|
|
505
505
|
import type { ConsolaInstance } from 'consola'
|
|
506
506
|
export const logger: ConsolaInstance
|
|
507
507
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
addTemplate({
|
|
511
|
-
filename: "types/ai-ready-db-provider.d.ts",
|
|
512
|
-
getContents: () => `declare module '#ai-ready/db-provider' {
|
|
508
|
+
|
|
509
|
+
declare module '#ai-ready-virtual/db-provider.mjs' {
|
|
513
510
|
import type { H3Event } from 'h3'
|
|
514
|
-
import type {
|
|
515
|
-
export function
|
|
511
|
+
import type { DrizzleDatabase } from '#ai-ready/server/db/drizzle/client'
|
|
512
|
+
export function createClient(event?: H3Event): Promise<DrizzleDatabase>
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
declare module '#ai-ready-virtual/db-schema.mjs' {
|
|
516
|
+
export * from '#ai-ready/server/db/schema/sqlite'
|
|
516
517
|
}
|
|
517
518
|
`
|
|
518
519
|
});
|
|
519
520
|
}
|
|
520
521
|
|
|
521
|
-
async function resolveDatabaseAdapter(type) {
|
|
522
|
-
const connectors = {
|
|
523
|
-
d1: "db0/connectors/cloudflare-d1",
|
|
524
|
-
libsql: "db0/connectors/libsql/node"
|
|
525
|
-
};
|
|
526
|
-
if (type !== "sqlite" && connectors[type]) {
|
|
527
|
-
return connectors[type];
|
|
528
|
-
}
|
|
529
|
-
if (process.versions.bun) {
|
|
530
|
-
return "db0/connectors/bun-sqlite";
|
|
531
|
-
}
|
|
532
|
-
const nodeVersion = Number.parseInt(process.versions.node?.split(".")[0] || "0");
|
|
533
|
-
if (nodeVersion >= 22) {
|
|
534
|
-
return "db0/connectors/node-sqlite";
|
|
535
|
-
}
|
|
536
|
-
return "db0/connectors/better-sqlite3";
|
|
537
|
-
}
|
|
538
522
|
function refineDatabaseConfig(config, rootDir) {
|
|
539
523
|
const type = config.type || "sqlite";
|
|
540
|
-
if (type === "sqlite") {
|
|
524
|
+
if (type === "sqlite" || type === "bun") {
|
|
541
525
|
const filename = config.filename || ".data/ai-ready/pages.db";
|
|
542
526
|
return {
|
|
543
|
-
type
|
|
527
|
+
type,
|
|
544
528
|
filename: isAbsolute(filename) ? filename : join$1(rootDir, filename)
|
|
545
529
|
};
|
|
546
530
|
}
|
|
@@ -550,6 +534,13 @@ function refineDatabaseConfig(config, rootDir) {
|
|
|
550
534
|
bindingName: config.bindingName || "DB"
|
|
551
535
|
};
|
|
552
536
|
}
|
|
537
|
+
if (type === "neon") {
|
|
538
|
+
return {
|
|
539
|
+
type: "neon",
|
|
540
|
+
url: config.url
|
|
541
|
+
// Will fallback to POSTGRES_URL at runtime
|
|
542
|
+
};
|
|
543
|
+
}
|
|
553
544
|
return {
|
|
554
545
|
type: "libsql",
|
|
555
546
|
url: config.url,
|
|
@@ -610,24 +601,29 @@ const module$1 = defineNuxtModule({
|
|
|
610
601
|
nuxt.options.alias["#ai-ready"] = resolve("./runtime");
|
|
611
602
|
const preset = String(nuxt.options.nitro.preset || "");
|
|
612
603
|
const isCloudflare = preset.startsWith("cloudflare");
|
|
613
|
-
const
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
604
|
+
const isVercel = preset === "vercel" || preset === "vercel-edge";
|
|
605
|
+
const isVercelEdge = preset === "vercel-edge";
|
|
606
|
+
const isBun = preset === "bun";
|
|
607
|
+
const hasPostgresUrl = !!(process.env.POSTGRES_URL || process.env.DATABASE_URL || config.database?.url);
|
|
608
|
+
let dbType = config.database?.type;
|
|
609
|
+
if (!dbType) {
|
|
610
|
+
if (isCloudflare) {
|
|
611
|
+
dbType = "d1";
|
|
612
|
+
logger.debug(`Auto-detected Cloudflare preset "${preset}", using D1 database`);
|
|
613
|
+
} else if (isVercel && hasPostgresUrl) {
|
|
614
|
+
dbType = "neon";
|
|
615
|
+
logger.debug(`Auto-detected Vercel preset with POSTGRES_URL, using Neon serverless driver`);
|
|
616
|
+
} else if (isVercelEdge) {
|
|
617
|
+
logger.warn(`Vercel Edge has no filesystem. Set POSTGRES_URL (Vercel Postgres) or configure database.type: 'libsql' for full functionality.`);
|
|
618
|
+
dbType = "neon";
|
|
619
|
+
} else if (isBun) {
|
|
620
|
+
dbType = "bun";
|
|
621
|
+
logger.debug(`Auto-detected Bun preset, using bun:sqlite driver`);
|
|
622
|
+
} else {
|
|
623
|
+
dbType = "sqlite";
|
|
624
|
+
}
|
|
627
625
|
}
|
|
628
|
-
nuxt.options.
|
|
629
|
-
nuxt.options.nitro.alias["#ai-ready/db-provider"] = providerPath;
|
|
630
|
-
if (!nuxt.options.mcp?.name) {
|
|
626
|
+
if (nuxt.options.mcp !== false && !nuxt.options.mcp?.name) {
|
|
631
627
|
nuxt.options.mcp = nuxt.options.mcp || {};
|
|
632
628
|
nuxt.options.mcp.name = useSiteConfig().name;
|
|
633
629
|
}
|
|
@@ -636,9 +632,11 @@ const module$1 = defineNuxtModule({
|
|
|
636
632
|
resolve("./runtime/server/utils")
|
|
637
633
|
);
|
|
638
634
|
if (typeof config.contentSignal === "object") {
|
|
639
|
-
nuxt.options.robots
|
|
640
|
-
nuxt.options.robots
|
|
641
|
-
|
|
635
|
+
const robotsOpts = nuxt.options.robots !== false ? nuxt.options.robots : {};
|
|
636
|
+
nuxt.options.robots = robotsOpts;
|
|
637
|
+
const groups = robotsOpts.groups || [];
|
|
638
|
+
robotsOpts.groups = groups;
|
|
639
|
+
groups.push({
|
|
642
640
|
userAgent: "*",
|
|
643
641
|
contentUsage: [`train-ai=${config.contentSignal.aiTrain ? "y" : "n"}`],
|
|
644
642
|
contentSignal: [`ai-train=${config.contentSignal.aiTrain ? "yes" : "no"}`, `search=${config.contentSignal.search ? "yes" : "no"}`, `ai-input=${config.contentSignal.aiInput ? "yes" : "no"}`]
|
|
@@ -669,7 +667,7 @@ const module$1 = defineNuxtModule({
|
|
|
669
667
|
});
|
|
670
668
|
const mcpLink = {
|
|
671
669
|
title: "MCP",
|
|
672
|
-
href: withSiteUrl(nuxt.options.mcp?.route || "/mcp"),
|
|
670
|
+
href: withSiteUrl(nuxt.options.mcp !== false && nuxt.options.mcp?.route || "/mcp"),
|
|
673
671
|
description: "Model Context Protocol server endpoint for AI agent integration."
|
|
674
672
|
};
|
|
675
673
|
if (defaultLlmsTxtSections[0]) {
|
|
@@ -737,11 +735,11 @@ const module$1 = defineNuxtModule({
|
|
|
737
735
|
if (config.cron && !nuxt.options.dev) {
|
|
738
736
|
const cronSchedule = "*/5 * * * *";
|
|
739
737
|
const preset2 = String(nitroConfig.preset || "");
|
|
740
|
-
const
|
|
738
|
+
const isVercel2 = preset2 === "vercel" || preset2 === "vercel-edge";
|
|
741
739
|
const isCloudflarePages = preset2 === "cloudflare-pages" || preset2 === "cloudflare_pages";
|
|
742
740
|
if (isCloudflarePages) {
|
|
743
741
|
logger.warn("Cloudflare Pages does not support cron. Use external cron to call /__ai-ready/cron instead.");
|
|
744
|
-
} else if (
|
|
742
|
+
} else if (isVercel2) {
|
|
745
743
|
nitroConfig.vercel = nitroConfig.vercel || {};
|
|
746
744
|
nitroConfig.vercel.config = nitroConfig.vercel.config || {};
|
|
747
745
|
nitroConfig.vercel.config.crons = nitroConfig.vercel.config.crons || [];
|
|
@@ -824,6 +822,17 @@ export const logger = createConsola({
|
|
|
824
822
|
level: ${config.debug ? 4 : 3},
|
|
825
823
|
})
|
|
826
824
|
`;
|
|
825
|
+
const providerMap = {
|
|
826
|
+
sqlite: "#ai-ready/server/db/drizzle/providers/sqlite",
|
|
827
|
+
bun: "#ai-ready/server/db/drizzle/providers/bun",
|
|
828
|
+
d1: "#ai-ready/server/db/drizzle/providers/d1",
|
|
829
|
+
libsql: "#ai-ready/server/db/drizzle/providers/libsql",
|
|
830
|
+
neon: "#ai-ready/server/db/drizzle/providers/neon"
|
|
831
|
+
};
|
|
832
|
+
const providerPath = providerMap[dbType] || providerMap.sqlite;
|
|
833
|
+
nitroConfig.virtual["#ai-ready-virtual/db-provider.mjs"] = `export { createClient } from '${providerPath}'`;
|
|
834
|
+
const schemaPath = dbType === "neon" ? "#ai-ready/server/db/schema/postgres" : "#ai-ready/server/db/schema/sqlite";
|
|
835
|
+
nitroConfig.virtual["#ai-ready-virtual/db-schema.mjs"] = `export * from '${schemaPath}'`;
|
|
827
836
|
});
|
|
828
837
|
const database = refineDatabaseConfig(config.database || {}, nuxt.options.rootDir);
|
|
829
838
|
nuxt.options.runtimeConfig["nuxt-ai-ready"] = {
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export type {
|
|
3
|
-
export { countPages, getStaleRoutes, isPageFresh, pruneStaleRoutes, queryPages, searchPages, seedRoutes, streamPages, upsertPage, } from './server/db/queries.js';
|
|
1
|
+
export { closeDrizzle, useDrizzle, useRawDb } from './server/db/index.js';
|
|
2
|
+
export type { DatabaseDialect, DrizzleDatabase, RawExecutor } from './server/db/index.js';
|
|
3
|
+
export { countPages, getPageLastmods, getStaleRoutes, isPageFresh, pruneStaleRoutes, queryPages, searchPages, seedRoutes, streamPages, upsertPage, } from './server/db/queries.js';
|
|
4
4
|
export type { CountPagesOptions, PageData, PageEntry, PageRow, QueryPagesOptions, SearchPagesOptions, SearchResult, StreamPagesOptions, UpsertPageInput, } from './server/db/queries.js';
|
|
5
5
|
export { indexPage, indexPageByRoute } from './server/utils/indexPage.js';
|
|
6
6
|
export type { IndexPageOptions, IndexPageResult } from './server/utils/indexPage.js';
|
package/dist/runtime/index.js
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type * as schema from '#ai-ready-virtual/db-schema.mjs';
|
|
2
|
+
import type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3';
|
|
3
|
+
import type { BunSQLiteDatabase } from 'drizzle-orm/bun-sqlite';
|
|
4
|
+
import type { DrizzleD1Database } from 'drizzle-orm/d1';
|
|
5
|
+
import type { LibSQLDatabase } from 'drizzle-orm/libsql';
|
|
6
|
+
import type { NeonHttpDatabase } from 'drizzle-orm/neon-http';
|
|
7
|
+
import type { H3Event } from 'h3';
|
|
8
|
+
export type DatabaseDialect = 'sqlite' | 'postgres';
|
|
9
|
+
type SQLiteDB = BetterSQLite3Database<typeof schema> | BunSQLiteDatabase<typeof schema> | LibSQLDatabase<typeof schema> | DrizzleD1Database<typeof schema>;
|
|
10
|
+
type PostgresDB = NeonHttpDatabase<typeof schema>;
|
|
11
|
+
export interface DrizzleDatabase {
|
|
12
|
+
dialect: DatabaseDialect;
|
|
13
|
+
db: SQLiteDB | PostgresDB;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Get Drizzle database instance
|
|
17
|
+
*/
|
|
18
|
+
export declare function useDrizzle(event?: H3Event): Promise<DrizzleDatabase>;
|
|
19
|
+
export declare function closeDrizzle(event?: H3Event): Promise<void>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { closeDriver } from "./raw.js";
|
|
2
|
+
const DB_CONTEXT_KEY = "_aiReadyDrizzle";
|
|
3
|
+
let fallbackClient;
|
|
4
|
+
export async function useDrizzle(event) {
|
|
5
|
+
if (event?.context?.[DB_CONTEXT_KEY]) {
|
|
6
|
+
return event.context[DB_CONTEXT_KEY];
|
|
7
|
+
}
|
|
8
|
+
if (!event && fallbackClient) {
|
|
9
|
+
return fallbackClient;
|
|
10
|
+
}
|
|
11
|
+
const { createClient } = await import("#ai-ready-virtual/db-provider.mjs");
|
|
12
|
+
const client = await createClient(event);
|
|
13
|
+
if (event?.context) {
|
|
14
|
+
event.context[DB_CONTEXT_KEY] = client;
|
|
15
|
+
} else {
|
|
16
|
+
fallbackClient = client;
|
|
17
|
+
}
|
|
18
|
+
return client;
|
|
19
|
+
}
|
|
20
|
+
export async function closeDrizzle(event) {
|
|
21
|
+
if (event?.context?.[DB_CONTEXT_KEY]) {
|
|
22
|
+
const client = event.context[DB_CONTEXT_KEY];
|
|
23
|
+
closeDriver(client.db);
|
|
24
|
+
delete event.context[DB_CONTEXT_KEY];
|
|
25
|
+
} else if (!event && fallbackClient) {
|
|
26
|
+
closeDriver(fallbackClient.db);
|
|
27
|
+
fallbackClient = void 0;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drizzle ORM database layer for nuxt-ai-ready
|
|
3
|
+
*/
|
|
4
|
+
export * from '#ai-ready-virtual/db-schema.mjs';
|
|
5
|
+
export { closeDrizzle, useDrizzle } from './client.js';
|
|
6
|
+
export type { DatabaseDialect, DrizzleDatabase } from './client.js';
|
|
7
|
+
export { completeCronRun, countPages, countPagesNeedingIndexNowSync, deleteInfoValue, deletePage, getAllPages, getContentHashes, getInfoValue, getNextSitemapToCrawl, getPageByRoute, getPageLastmods, getPagesNeedingIndexNowSync, getPendingPages, getRecentCronRuns, getSitemapStatus, initSchema, markIndexNowSynced, markPageIndexed, markRoutesPending, markSitemapCrawled, markSitemapError, resetSitemapErrors, searchPages, seedRoutes, setInfoValue, startCronRun, syncSitemaps, upsertPage, } from './queries.js';
|
|
8
|
+
export type { CronRunOutput, PageInput, PageMetaOutput, PageOutput, SitemapOutput, } from './queries.js';
|
|
9
|
+
export { useRawDb } from './raw.js';
|
|
10
|
+
export type { RawExecutor } from './raw.js';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export * from "#ai-ready-virtual/db-schema.mjs";
|
|
2
|
+
export { closeDrizzle, useDrizzle } from "./client.js";
|
|
3
|
+
export {
|
|
4
|
+
completeCronRun,
|
|
5
|
+
countPages,
|
|
6
|
+
countPagesNeedingIndexNowSync,
|
|
7
|
+
deleteInfoValue,
|
|
8
|
+
deletePage,
|
|
9
|
+
getAllPages,
|
|
10
|
+
getContentHashes,
|
|
11
|
+
getInfoValue,
|
|
12
|
+
getNextSitemapToCrawl,
|
|
13
|
+
getPageByRoute,
|
|
14
|
+
getPageLastmods,
|
|
15
|
+
getPagesNeedingIndexNowSync,
|
|
16
|
+
getPendingPages,
|
|
17
|
+
getRecentCronRuns,
|
|
18
|
+
getSitemapStatus,
|
|
19
|
+
initSchema,
|
|
20
|
+
markIndexNowSynced,
|
|
21
|
+
markPageIndexed,
|
|
22
|
+
markRoutesPending,
|
|
23
|
+
markSitemapCrawled,
|
|
24
|
+
markSitemapError,
|
|
25
|
+
resetSitemapErrors,
|
|
26
|
+
searchPages,
|
|
27
|
+
seedRoutes,
|
|
28
|
+
setInfoValue,
|
|
29
|
+
startCronRun,
|
|
30
|
+
syncSitemaps,
|
|
31
|
+
upsertPage
|
|
32
|
+
} from "./queries.js";
|
|
33
|
+
export { useRawDb } from "./raw.js";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { mkdir } from "node:fs/promises";
|
|
2
|
+
import * as schema from "#ai-ready-virtual/db-schema.mjs";
|
|
3
|
+
import { Database } from "bun:sqlite";
|
|
4
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
5
|
+
import { useRuntimeConfig } from "nitropack/runtime";
|
|
6
|
+
import { dirname } from "pathe";
|
|
7
|
+
import { logger } from "../../../logger.js";
|
|
8
|
+
import { registerDriver } from "../raw.js";
|
|
9
|
+
export async function createClient(event) {
|
|
10
|
+
const config = useRuntimeConfig(event)["nuxt-ai-ready"];
|
|
11
|
+
const dbPath = config.database.filename || ".data/ai-ready/pages.db";
|
|
12
|
+
logger.debug(`[drizzle] Opening Bun SQLite database: ${dbPath}`);
|
|
13
|
+
await mkdir(dirname(dbPath), { recursive: true }).catch((err) => {
|
|
14
|
+
if (err.code === "EROFS" || err.code === "EACCES") {
|
|
15
|
+
throw new Error(
|
|
16
|
+
`[ai-ready] Cannot create database directory (read-only filesystem). On Vercel, set database.type: 'neon'. On Cloudflare, use 'd1'. Or configure database.url for LibSQL/Turso.`
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
throw err;
|
|
20
|
+
});
|
|
21
|
+
const sqlite = new Database(dbPath);
|
|
22
|
+
const db = drizzle(sqlite, { schema });
|
|
23
|
+
registerDriver(db, "better-sqlite3", sqlite);
|
|
24
|
+
return { dialect: "sqlite", db };
|
|
25
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as schema from "#ai-ready-virtual/db-schema.mjs";
|
|
2
|
+
import { drizzle } from "drizzle-orm/d1";
|
|
3
|
+
import { useRuntimeConfig } from "nitropack/runtime";
|
|
4
|
+
import { logger } from "../../../logger.js";
|
|
5
|
+
import { registerDriver } from "../raw.js";
|
|
6
|
+
export async function createClient(event) {
|
|
7
|
+
const config = useRuntimeConfig(event)["nuxt-ai-ready"];
|
|
8
|
+
const bindingName = config.database.bindingName || "DB";
|
|
9
|
+
logger.debug(`[drizzle] Using D1 binding: ${bindingName}`);
|
|
10
|
+
const cfEnv = event?.context?.cloudflare?.env;
|
|
11
|
+
const globalEnv = globalThis.__env__;
|
|
12
|
+
const d1 = cfEnv?.[bindingName] || globalEnv?.[bindingName];
|
|
13
|
+
if (!d1) {
|
|
14
|
+
throw new Error(`[ai-ready] D1 binding "${bindingName}" not found`);
|
|
15
|
+
}
|
|
16
|
+
const db = drizzle(d1, { schema });
|
|
17
|
+
registerDriver(db, "d1", d1);
|
|
18
|
+
return { dialect: "sqlite", db };
|
|
19
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as schema from "#ai-ready-virtual/db-schema.mjs";
|
|
2
|
+
import { createClient as createLibSQLClient } from "@libsql/client";
|
|
3
|
+
import { drizzle } from "drizzle-orm/libsql";
|
|
4
|
+
import { useRuntimeConfig } from "nitropack/runtime";
|
|
5
|
+
import { logger } from "../../../logger.js";
|
|
6
|
+
import { registerDriver } from "../raw.js";
|
|
7
|
+
export async function createClient(event) {
|
|
8
|
+
const config = useRuntimeConfig(event)["nuxt-ai-ready"];
|
|
9
|
+
const dbUrl = config.database.url || `file:${config.database.filename || ".data/ai-ready/pages.db"}`;
|
|
10
|
+
logger.debug(`[drizzle] Connecting to LibSQL: ${dbUrl}`);
|
|
11
|
+
const client = createLibSQLClient({ url: dbUrl, authToken: config.database.authToken });
|
|
12
|
+
const db = drizzle(client, { schema });
|
|
13
|
+
registerDriver(db, "libsql", client);
|
|
14
|
+
return { dialect: "sqlite", db };
|
|
15
|
+
}
|