nuxt-ai-ready 0.10.12 → 0.12.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.
Files changed (53) hide show
  1. package/README.md +7 -1
  2. package/dist/cli.mjs +11 -8
  3. package/dist/module.d.mts +7 -1
  4. package/dist/module.json +1 -1
  5. package/dist/module.mjs +80 -74
  6. package/dist/runtime/index.d.ts +3 -3
  7. package/dist/runtime/index.js +2 -1
  8. package/dist/runtime/server/db/drizzle/client.d.ts +20 -0
  9. package/dist/runtime/server/db/drizzle/client.js +29 -0
  10. package/dist/runtime/server/db/drizzle/index.d.ts +10 -0
  11. package/dist/runtime/server/db/drizzle/index.js +33 -0
  12. package/dist/runtime/server/db/drizzle/providers/bun.d.ts +7 -0
  13. package/dist/runtime/server/db/drizzle/providers/bun.js +25 -0
  14. package/dist/runtime/server/db/drizzle/providers/d1.d.ts +7 -0
  15. package/dist/runtime/server/db/drizzle/providers/d1.js +19 -0
  16. package/dist/runtime/server/db/drizzle/providers/libsql.d.ts +7 -0
  17. package/dist/runtime/server/db/drizzle/providers/libsql.js +15 -0
  18. package/dist/runtime/server/db/drizzle/providers/neon.d.ts +7 -0
  19. package/dist/runtime/server/db/drizzle/providers/neon.js +18 -0
  20. package/dist/runtime/server/db/drizzle/providers/sqlite.d.ts +8 -0
  21. package/dist/runtime/server/db/drizzle/providers/sqlite.js +25 -0
  22. package/dist/runtime/server/db/drizzle/queries.d.ts +190 -0
  23. package/dist/runtime/server/db/drizzle/queries.js +573 -0
  24. package/dist/runtime/server/db/drizzle/raw.d.ts +28 -0
  25. package/dist/runtime/server/db/drizzle/raw.js +93 -0
  26. package/dist/runtime/server/db/index.d.ts +2 -12
  27. package/dist/runtime/server/db/index.js +1 -36
  28. package/dist/runtime/server/db/queries.d.ts +5 -0
  29. package/dist/runtime/server/db/queries.js +27 -3
  30. package/dist/runtime/server/db/schema/postgres.d.ts +1500 -0
  31. package/dist/runtime/server/db/schema/postgres.js +66 -0
  32. package/dist/runtime/server/db/schema/sqlite.d.ts +1580 -0
  33. package/dist/runtime/server/db/schema/sqlite.js +66 -0
  34. package/dist/runtime/server/db/schema-sql.d.ts +6 -2
  35. package/dist/runtime/server/db/schema-sql.js +73 -92
  36. package/dist/runtime/server/db/shared.d.ts +1 -6
  37. package/dist/runtime/server/db/shared.js +0 -19
  38. package/dist/runtime/server/plugins/db-lifecycle.js +3 -3
  39. package/dist/runtime/server/plugins/sitemap-seeder.js +19 -2
  40. package/dist/runtime/server/routes/__ai-ready/restore.post.js +2 -2
  41. package/dist/runtime/server/routes/__ai-ready-debug.get.js +4 -4
  42. package/dist/runtime/server/utils/checkStale.js +19 -31
  43. package/dist/runtime/server/utils/indexnow.js +12 -13
  44. package/dist/runtime/types.d.ts +10 -4
  45. package/package.json +41 -32
  46. package/dist/runtime/server/db/provider/d1.d.ts +0 -3
  47. package/dist/runtime/server/db/provider/d1.js +0 -7
  48. package/dist/runtime/server/db/provider/libsql.d.ts +0 -3
  49. package/dist/runtime/server/db/provider/libsql.js +0 -9
  50. package/dist/runtime/server/db/provider/sqlite-bun.d.ts +0 -3
  51. package/dist/runtime/server/db/provider/sqlite-bun.js +0 -13
  52. package/dist/runtime/server/db/provider/sqlite-node.d.ts +0 -3
  53. package/dist/runtime/server/db/provider/sqlite-node.js +0 -13
package/README.md CHANGED
@@ -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 (default: true)",
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 { ModuleOptions, LlmsTxtConfig } from '../dist/runtime/types.js';
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
@@ -4,7 +4,7 @@
4
4
  "nuxt": ">=4.0.0"
5
5
  },
6
6
  "configKey": "aiReady",
7
- "version": "0.10.12",
7
+ "version": "0.12.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
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, addTemplate, addTypeTemplate, defineNuxtModule, createResolver, hasNuxtModule, addServerHandler, addPlugin, addServerPlugin } from '@nuxt/kit';
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 { createAdapter, initSchema, computeContentHash, insertPage, queryAllPages, exportDbDump } from '../dist/runtime/server/db/shared.js';
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.stop("\u274C Invalid API key");
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.stop("\u274C No active subscription");
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.stop("\u26A0\uFE0F License verification skipped (network issue)");
75
+ spinner.cancel("License verification skipped (network issue)");
76
76
  return;
77
77
  }
78
78
  spinner.stop("License verified \u2713");
@@ -140,13 +140,21 @@ async function initCrawler(state) {
140
140
  if (state.dbPath) {
141
141
  logger.debug(`Creating directory for SQLite: ${dirname(state.dbPath)}`);
142
142
  await mkdir(dirname(state.dbPath), { recursive: true });
143
- const nodeVersion = Number.parseInt(process.versions.node?.split(".")[0] || "0");
144
- const connectorPath = nodeVersion >= 22 ? "db0/connectors/node-sqlite" : "db0/connectors/better-sqlite3";
145
- const { default: connectorFn } = await import(connectorPath);
146
- const connector = connectorFn({ path: state.dbPath });
147
- state.db = createAdapter(connector);
148
- await initSchema(state.db);
149
- logger.debug(`Crawler initialized with SQLite at ${state.dbPath} using ${connectorPath}`);
143
+ const { default: Database } = await import('better-sqlite3');
144
+ const sqlite = new Database(state.dbPath);
145
+ const db = {
146
+ all: async (sql, params = []) => sqlite.prepare(sql).all(...params),
147
+ first: async (sql, params = []) => sqlite.prepare(sql).get(...params),
148
+ exec: async (sql, params = []) => {
149
+ sqlite.prepare(sql).run(...params);
150
+ },
151
+ close: async () => {
152
+ sqlite.close();
153
+ }
154
+ };
155
+ state.db = db;
156
+ await initSchema(db);
157
+ logger.debug(`Crawler initialized with SQLite at ${state.dbPath}`);
150
158
  }
151
159
  if (state.llmsFullTxtPath) {
152
160
  logger.debug(`Creating directory for llms-full.txt: ${dirname(state.llmsFullTxtPath)}`);
@@ -430,7 +438,7 @@ function setupPrerenderHandler(options, dbPath, siteInfo, llmsTxtConfig, indexNo
430
438
  await writeLlmsFiles();
431
439
  state.prerenderedRoutes.clear();
432
440
  if (state.db)
433
- await state.db.close();
441
+ await state.db.close?.();
434
442
  });
435
443
  } else if (usePrerenderHook) {
436
444
  nitro.hooks.hook("prerender:done", async () => {
@@ -444,28 +452,17 @@ function setupPrerenderHandler(options, dbPath, siteInfo, llmsTxtConfig, indexNo
444
452
  await writeLlmsFiles();
445
453
  state.prerenderedRoutes.clear();
446
454
  if (state.db)
447
- await state.db.close();
455
+ await state.db.close?.();
448
456
  });
449
457
  }
450
458
  });
451
459
  }
452
460
 
453
461
  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
462
  addTypeTemplate({
464
463
  filename: "types/nuxt-ai-ready-augments.d.ts",
465
464
  getContents: () => `// Generated by nuxt-ai-ready
466
- /// <reference path="./ai-ready-adapter.d.ts" />
467
465
  /// <reference path="./ai-ready-virtual.d.ts" />
468
- /// <reference path="./ai-ready-db-provider.d.ts" />
469
466
  import type { MarkdownContext, PageIndexedContext } from 'nuxt-ai-ready'
470
467
  import type { HTMLToMarkdownOptions } from 'mdream'
471
468
 
@@ -505,42 +502,26 @@ declare module '#ai-ready-virtual/logger.mjs' {
505
502
  import type { ConsolaInstance } from 'consola'
506
503
  export const logger: ConsolaInstance
507
504
  }
508
- `
509
- });
510
- addTemplate({
511
- filename: "types/ai-ready-db-provider.d.ts",
512
- getContents: () => `declare module '#ai-ready/db-provider' {
505
+
506
+ declare module '#ai-ready-virtual/db-provider.mjs' {
513
507
  import type { H3Event } from 'h3'
514
- import type { Connector } from 'db0'
515
- export function createConnector(event?: H3Event): Promise<Connector>
508
+ import type { DrizzleDatabase } from '#ai-ready/server/db/drizzle/client'
509
+ export function createClient(event?: H3Event): Promise<DrizzleDatabase>
510
+ }
511
+
512
+ declare module '#ai-ready-virtual/db-schema.mjs' {
513
+ export * from '#ai-ready/server/db/schema/sqlite'
516
514
  }
517
515
  `
518
516
  });
519
517
  }
520
518
 
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
519
  function refineDatabaseConfig(config, rootDir) {
539
520
  const type = config.type || "sqlite";
540
- if (type === "sqlite") {
521
+ if (type === "sqlite" || type === "bun") {
541
522
  const filename = config.filename || ".data/ai-ready/pages.db";
542
523
  return {
543
- type: "sqlite",
524
+ type,
544
525
  filename: isAbsolute(filename) ? filename : join$1(rootDir, filename)
545
526
  };
546
527
  }
@@ -550,6 +531,13 @@ function refineDatabaseConfig(config, rootDir) {
550
531
  bindingName: config.bindingName || "DB"
551
532
  };
552
533
  }
534
+ if (type === "neon") {
535
+ return {
536
+ type: "neon",
537
+ url: config.url
538
+ // Will fallback to POSTGRES_URL at runtime
539
+ };
540
+ }
553
541
  return {
554
542
  type: "libsql",
555
543
  url: config.url,
@@ -610,24 +598,29 @@ const module$1 = defineNuxtModule({
610
598
  nuxt.options.alias["#ai-ready"] = resolve("./runtime");
611
599
  const preset = String(nuxt.options.nitro.preset || "");
612
600
  const isCloudflare = preset.startsWith("cloudflare");
613
- const dbType = config.database?.type || (isCloudflare ? "d1" : "sqlite");
614
- if (isCloudflare && !config.database?.type) {
615
- logger.debug(`Auto-detected Cloudflare preset "${preset}", using D1 database`);
616
- }
617
- const adapterPath = await resolveDatabaseAdapter(dbType);
618
- nuxt.options.alias["#ai-ready/adapter"] = adapterPath;
619
- nuxt.options.nitro.alias["#ai-ready/adapter"] = adapterPath;
620
- let providerPath = resolve("./runtime/server/db/provider/sqlite-node");
621
- if (dbType === "d1") {
622
- providerPath = resolve("./runtime/server/db/provider/d1");
623
- } else if (dbType === "libsql") {
624
- providerPath = resolve("./runtime/server/db/provider/libsql");
625
- } else if (process.versions.bun) {
626
- providerPath = resolve("./runtime/server/db/provider/sqlite-bun");
601
+ const isVercel = preset === "vercel" || preset === "vercel-edge";
602
+ const isVercelEdge = preset === "vercel-edge";
603
+ const isBun = preset === "bun";
604
+ const hasPostgresUrl = !!(process.env.POSTGRES_URL || process.env.DATABASE_URL || config.database?.url);
605
+ let dbType = config.database?.type;
606
+ if (!dbType) {
607
+ if (isCloudflare) {
608
+ dbType = "d1";
609
+ logger.debug(`Auto-detected Cloudflare preset "${preset}", using D1 database`);
610
+ } else if (isVercel && hasPostgresUrl) {
611
+ dbType = "neon";
612
+ logger.debug(`Auto-detected Vercel preset with POSTGRES_URL, using Neon serverless driver`);
613
+ } else if (isVercelEdge) {
614
+ logger.warn(`Vercel Edge has no filesystem. Set POSTGRES_URL (Vercel Postgres) or configure database.type: 'libsql' for full functionality.`);
615
+ dbType = "neon";
616
+ } else if (isBun) {
617
+ dbType = "bun";
618
+ logger.debug(`Auto-detected Bun preset, using bun:sqlite driver`);
619
+ } else {
620
+ dbType = "sqlite";
621
+ }
627
622
  }
628
- nuxt.options.alias["#ai-ready/db-provider"] = providerPath;
629
- nuxt.options.nitro.alias["#ai-ready/db-provider"] = providerPath;
630
- if (!nuxt.options.mcp?.name) {
623
+ if (nuxt.options.mcp !== false && !nuxt.options.mcp?.name) {
631
624
  nuxt.options.mcp = nuxt.options.mcp || {};
632
625
  nuxt.options.mcp.name = useSiteConfig().name;
633
626
  }
@@ -636,9 +629,11 @@ const module$1 = defineNuxtModule({
636
629
  resolve("./runtime/server/utils")
637
630
  );
638
631
  if (typeof config.contentSignal === "object") {
639
- nuxt.options.robots = nuxt.options.robots || {};
640
- nuxt.options.robots.groups = nuxt.options.robots.groups || [];
641
- nuxt.options.robots.groups.push({
632
+ const robotsOpts = nuxt.options.robots !== false ? nuxt.options.robots : {};
633
+ nuxt.options.robots = robotsOpts;
634
+ const groups = robotsOpts.groups || [];
635
+ robotsOpts.groups = groups;
636
+ groups.push({
642
637
  userAgent: "*",
643
638
  contentUsage: [`train-ai=${config.contentSignal.aiTrain ? "y" : "n"}`],
644
639
  contentSignal: [`ai-train=${config.contentSignal.aiTrain ? "yes" : "no"}`, `search=${config.contentSignal.search ? "yes" : "no"}`, `ai-input=${config.contentSignal.aiInput ? "yes" : "no"}`]
@@ -669,7 +664,7 @@ const module$1 = defineNuxtModule({
669
664
  });
670
665
  const mcpLink = {
671
666
  title: "MCP",
672
- href: withSiteUrl(nuxt.options.mcp?.route || "/mcp"),
667
+ href: withSiteUrl(nuxt.options.mcp !== false && nuxt.options.mcp?.route || "/mcp"),
673
668
  description: "Model Context Protocol server endpoint for AI agent integration."
674
669
  };
675
670
  if (defaultLlmsTxtSections[0]) {
@@ -737,11 +732,11 @@ const module$1 = defineNuxtModule({
737
732
  if (config.cron && !nuxt.options.dev) {
738
733
  const cronSchedule = "*/5 * * * *";
739
734
  const preset2 = String(nitroConfig.preset || "");
740
- const isVercel = preset2 === "vercel" || preset2 === "vercel-edge";
735
+ const isVercel2 = preset2 === "vercel" || preset2 === "vercel-edge";
741
736
  const isCloudflarePages = preset2 === "cloudflare-pages" || preset2 === "cloudflare_pages";
742
737
  if (isCloudflarePages) {
743
738
  logger.warn("Cloudflare Pages does not support cron. Use external cron to call /__ai-ready/cron instead.");
744
- } else if (isVercel) {
739
+ } else if (isVercel2) {
745
740
  nitroConfig.vercel = nitroConfig.vercel || {};
746
741
  nitroConfig.vercel.config = nitroConfig.vercel.config || {};
747
742
  nitroConfig.vercel.config.crons = nitroConfig.vercel.config.crons || [];
@@ -824,6 +819,17 @@ export const logger = createConsola({
824
819
  level: ${config.debug ? 4 : 3},
825
820
  })
826
821
  `;
822
+ const providerMap = {
823
+ sqlite: "#ai-ready/server/db/drizzle/providers/sqlite",
824
+ bun: "#ai-ready/server/db/drizzle/providers/bun",
825
+ d1: "#ai-ready/server/db/drizzle/providers/d1",
826
+ libsql: "#ai-ready/server/db/drizzle/providers/libsql",
827
+ neon: "#ai-ready/server/db/drizzle/providers/neon"
828
+ };
829
+ const providerPath = providerMap[dbType] || providerMap.sqlite;
830
+ nitroConfig.virtual["#ai-ready-virtual/db-provider.mjs"] = `export { createClient } from '${providerPath}'`;
831
+ const schemaPath = dbType === "neon" ? "#ai-ready/server/db/schema/postgres" : "#ai-ready/server/db/schema/sqlite";
832
+ nitroConfig.virtual["#ai-ready-virtual/db-schema.mjs"] = `export * from '${schemaPath}'`;
827
833
  });
828
834
  const database = refineDatabaseConfig(config.database || {}, nuxt.options.rootDir);
829
835
  nuxt.options.runtimeConfig["nuxt-ai-ready"] = {
@@ -1,6 +1,6 @@
1
- export { useDatabase } from './server/db/index.js';
2
- export type { DatabaseAdapter } from './server/db/index.js';
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';
@@ -1,6 +1,7 @@
1
- export { useDatabase } from "./server/db/index.js";
1
+ export { closeDrizzle, useDrizzle, useRawDb } from "./server/db/index.js";
2
2
  export {
3
3
  countPages,
4
+ getPageLastmods,
4
5
  getStaleRoutes,
5
6
  isPageFresh,
6
7
  pruneStaleRoutes,
@@ -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,7 @@
1
+ import type { H3Event } from 'h3';
2
+ export declare function createClient(event?: H3Event): Promise<{
3
+ dialect: "sqlite";
4
+ db: import("drizzle-orm/bun-sqlite").BunSQLiteDatabase<any> & {
5
+ $client: any;
6
+ };
7
+ }>;
@@ -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,7 @@
1
+ import type { H3Event } from 'h3';
2
+ export declare function createClient(event?: H3Event): Promise<{
3
+ dialect: "sqlite";
4
+ db: import("drizzle-orm/d1").DrizzleD1Database<any> & {
5
+ $client: any;
6
+ };
7
+ }>;
@@ -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,7 @@
1
+ import type { H3Event } from 'h3';
2
+ export declare function createClient(event?: H3Event): Promise<{
3
+ dialect: "sqlite";
4
+ db: import("drizzle-orm/libsql").LibSQLDatabase<any> & {
5
+ $client: import("@libsql/client").Client;
6
+ };
7
+ }>;
@@ -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
+ }
@@ -0,0 +1,7 @@
1
+ import type { H3Event } from 'h3';
2
+ export declare function createClient(event?: H3Event): Promise<{
3
+ dialect: "postgres";
4
+ db: import("drizzle-orm/neon-http").NeonHttpDatabase<any> & {
5
+ $client: import("@neondatabase/serverless").NeonQueryFunction<false, false>;
6
+ };
7
+ }>;
@@ -0,0 +1,18 @@
1
+ import * as schema from "#ai-ready-virtual/db-schema.mjs";
2
+ import { neon } from "@neondatabase/serverless";
3
+ import { drizzle } from "drizzle-orm/neon-http";
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 connectionString = config.database.url || process.env.POSTGRES_URL || process.env.DATABASE_URL;
10
+ if (!connectionString) {
11
+ throw new Error("[ai-ready] Missing database URL. Set POSTGRES_URL or configure database.url");
12
+ }
13
+ logger.debug(`[drizzle] Connecting to Neon Postgres`);
14
+ const sqlFn = neon(connectionString);
15
+ const db = drizzle(sqlFn, { schema });
16
+ registerDriver(db, "neon", sqlFn);
17
+ return { dialect: "postgres", db };
18
+ }
@@ -0,0 +1,8 @@
1
+ import type { H3Event } from 'h3';
2
+ import Database from 'better-sqlite3';
3
+ export declare function createClient(event?: H3Event): Promise<{
4
+ dialect: "sqlite";
5
+ db: import("drizzle-orm/better-sqlite3").BetterSQLite3Database<any> & {
6
+ $client: Database.Database;
7
+ };
8
+ }>;