geo-ai-next 0.1.1 → 0.2.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 +243 -8
- package/dist/cli.mjs +102 -0
- package/dist/index.cjs +63 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -1
- package/dist/index.d.ts +35 -1
- package/dist/index.mjs +43 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -3
package/README.md
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://npmjs.com/package/geo-ai-next)
|
|
4
4
|
|
|
5
|
-
Part of the [GEO AI
|
|
5
|
+
Part of the [GEO AI – AI Search Optimization](https://www.geoai.run) ecosystem. [GitHub](https://github.com/madeburo/GEO-AI)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Next.js integration for [geo-ai-core](https://npmjs.com/package/geo-ai-core) — static file generation, middleware, and App Router route handler for `llms.txt` / `llms-full.txt`.
|
|
8
|
+
|
|
9
|
+
Try the analyzer at [geoai.run/analyze](https://www.geoai.run/analyze)
|
|
8
10
|
|
|
9
11
|
## Installation
|
|
10
12
|
|
|
@@ -14,9 +16,237 @@ npm install geo-ai-next
|
|
|
14
16
|
|
|
15
17
|
Peer dependency: `next >= 16`
|
|
16
18
|
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Recommended Production Setup
|
|
22
|
+
|
|
23
|
+
The shortest path to working GEO AI files in production:
|
|
24
|
+
|
|
25
|
+
**1.** Create `geo-ai.config.mjs` in your project root
|
|
26
|
+
**2.** Add the build script to `package.json`
|
|
27
|
+
**3.** Run `npm run build` — files appear in `public/`
|
|
28
|
+
**4.** Deploy — done
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"scripts": {
|
|
33
|
+
"geo:generate": "geo-ai-generate",
|
|
34
|
+
"build": "npm run geo:generate && next build"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
// geo-ai.config.mjs
|
|
41
|
+
export default {
|
|
42
|
+
siteName: 'My Site',
|
|
43
|
+
siteUrl: 'https://example.com',
|
|
44
|
+
provider: { Pages: [{ title: 'Home', url: '/', description: 'Welcome' }] },
|
|
45
|
+
crawlers: 'all',
|
|
46
|
+
};
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
After build, verify:
|
|
50
|
+
```bash
|
|
51
|
+
ls public/llms.txt public/llms-full.txt # both files present
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Full details and options in the sections below.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## llms.txt vs llms-full.txt
|
|
59
|
+
|
|
60
|
+
Both files follow the [llms.txt standard](https://llmstxt.org) for making site content readable by AI search engines.
|
|
61
|
+
|
|
62
|
+
| File | Purpose | Content |
|
|
63
|
+
|------|---------|---------|
|
|
64
|
+
| `llms.txt` | Concise index for AI crawlers | Site name, description, crawler rules, resource list with titles, URLs, and short descriptions |
|
|
65
|
+
| `llms-full.txt` | Extended version for deep indexing | Everything in `llms.txt` plus full page content, product pricing, availability, and variants |
|
|
66
|
+
|
|
67
|
+
AI crawlers like GPTBot and ClaudeBot discover `llms.txt` first. If they need more detail — for example to answer a product question — they fetch `llms-full.txt`. Serving both maximizes your site's visibility in AI search results.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Static File Generation (Recommended)
|
|
72
|
+
|
|
73
|
+
The most reliable approach for production. Generates `public/llms.txt` and `public/llms-full.txt` as static files before `next build` — Next.js serves them automatically, no middleware needed.
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
// scripts/generate-llms.ts
|
|
77
|
+
import { generateLlmsFiles } from 'geo-ai-next';
|
|
78
|
+
|
|
79
|
+
await generateLlmsFiles({
|
|
80
|
+
siteName: 'My Site',
|
|
81
|
+
siteUrl: 'https://example.com',
|
|
82
|
+
siteDescription: 'AI-optimized site description',
|
|
83
|
+
provider: {
|
|
84
|
+
Pages: [
|
|
85
|
+
{ title: 'Home', url: '/', description: 'Welcome page' },
|
|
86
|
+
{ title: 'About', url: '/about', description: 'About us' },
|
|
87
|
+
],
|
|
88
|
+
Products: [
|
|
89
|
+
{ title: 'Widget', url: '/products/widget', description: 'A great widget', price: '$29' },
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
crawlers: 'all',
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Add to `package.json`:
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"scripts": {
|
|
101
|
+
"geo:generate": "geo-ai-generate",
|
|
102
|
+
"build": "npm run geo:generate && next build"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
After `npm run build`, both files are written to `public/` and served at:
|
|
108
|
+
- `https://yoursite.com/llms.txt`
|
|
109
|
+
- `https://yoursite.com/llms-full.txt`
|
|
110
|
+
|
|
111
|
+
### Options
|
|
112
|
+
|
|
113
|
+
| Option | Type | Default | Description |
|
|
114
|
+
|--------|------|---------|-------------|
|
|
115
|
+
| `outDir` | `string` | `'public'` | Output directory (relative to cwd) |
|
|
116
|
+
| `locale` | `string` | — | Locale for content generation |
|
|
117
|
+
| + all `GeoAIConfig` options | | | `siteName`, `siteUrl`, `provider`, `crawlers`, `cache`, etc. |
|
|
118
|
+
|
|
119
|
+
### CLI
|
|
120
|
+
|
|
121
|
+
The `geo-ai-generate` binary is included in the package. It reads `geo-ai.config.mjs` from your project root by default.
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# Run directly (reads geo-ai.config.mjs)
|
|
125
|
+
npx geo-ai-generate
|
|
126
|
+
|
|
127
|
+
# Custom config path
|
|
128
|
+
npx geo-ai-generate --config ./config/geo-ai.mjs
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Example `geo-ai.config.mjs`:
|
|
132
|
+
|
|
133
|
+
```js
|
|
134
|
+
// geo-ai.config.mjs
|
|
135
|
+
export default {
|
|
136
|
+
siteName: 'My Site',
|
|
137
|
+
siteUrl: 'https://example.com',
|
|
138
|
+
provider: {
|
|
139
|
+
Pages: [
|
|
140
|
+
{ title: 'Home', url: '/', description: 'Welcome' },
|
|
141
|
+
{ title: 'About', url: '/about', description: 'About us' },
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
crawlers: 'all',
|
|
145
|
+
};
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
With this config file in place, your `package.json` scripts become:
|
|
149
|
+
|
|
150
|
+
```json
|
|
151
|
+
{
|
|
152
|
+
"scripts": {
|
|
153
|
+
"geo:generate": "geo-ai-generate",
|
|
154
|
+
"build": "npm run geo:generate && next build"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Troubleshooting: 404 on `/llms.txt`
|
|
160
|
+
|
|
161
|
+
1. Make sure `generateLlmsFiles()` runs before `next build`
|
|
162
|
+
2. Check that `public/llms.txt` exists after generation
|
|
163
|
+
3. Vercel and Netlify serve `public/` automatically — no extra config needed
|
|
164
|
+
4. For custom servers, ensure static file serving is configured for the `public/` directory
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## End-to-End Example
|
|
169
|
+
|
|
170
|
+
Minimal Next.js app that generates and serves both files:
|
|
171
|
+
|
|
172
|
+
**1. Install**
|
|
173
|
+
```bash
|
|
174
|
+
npm install geo-ai-next
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**2. Create `geo-ai.config.mjs`**
|
|
178
|
+
```js
|
|
179
|
+
export default {
|
|
180
|
+
siteName: 'Acme Store',
|
|
181
|
+
siteUrl: 'https://acme.example.com',
|
|
182
|
+
siteDescription: 'Quality widgets for every need',
|
|
183
|
+
provider: {
|
|
184
|
+
Pages: [
|
|
185
|
+
{ title: 'Home', url: '/', description: 'Welcome to Acme Store' },
|
|
186
|
+
{ title: 'About', url: '/about', description: 'Our story' },
|
|
187
|
+
],
|
|
188
|
+
Products: [
|
|
189
|
+
{
|
|
190
|
+
title: 'Classic Widget',
|
|
191
|
+
url: '/products/classic',
|
|
192
|
+
description: 'Our best-selling widget',
|
|
193
|
+
price: '$29.99',
|
|
194
|
+
available: true,
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
},
|
|
198
|
+
crawlers: 'all',
|
|
199
|
+
};
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**3. Add scripts to `package.json`**
|
|
203
|
+
```json
|
|
204
|
+
{
|
|
205
|
+
"scripts": {
|
|
206
|
+
"geo:generate": "geo-ai-generate",
|
|
207
|
+
"build": "npm run geo:generate && next build"
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**4. Run build**
|
|
213
|
+
```bash
|
|
214
|
+
npm run build
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Output:
|
|
218
|
+
```
|
|
219
|
+
[geo-ai] Generating llms files → /your-project/public
|
|
220
|
+
[geo-ai] ✓ /your-project/public/llms.txt (843 bytes)
|
|
221
|
+
[geo-ai] ✓ /your-project/public/llms-full.txt (1204 bytes)
|
|
222
|
+
[geo-ai] Done.
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**5. Verify**
|
|
226
|
+
```bash
|
|
227
|
+
curl https://acme.example.com/llms.txt
|
|
228
|
+
curl https://acme.example.com/llms-full.txt
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Both return `200 OK` with `text/plain` content — no middleware, no route handler needed.
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Runtime Serving (Alternative)
|
|
236
|
+
|
|
237
|
+
If you were already using middleware or a route handler, they continue to work. Static generation is the recommended production approach, but runtime serving is still valid for dynamic use cases like per-request locale or content that changes too frequently to regenerate at build time.
|
|
238
|
+
|
|
239
|
+
| Approach | When to use |
|
|
240
|
+
|----------|-------------|
|
|
241
|
+
| Static generation (`generateLlmsFiles`) | Production default — works on Vercel, Netlify, any static host |
|
|
242
|
+
| Middleware (`geoAIMiddleware`) | Dynamic content per-request, edge locale detection |
|
|
243
|
+
| Route handler (`createLlmsHandler`) | Custom route path, App Router, programmatic control |
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
17
247
|
## Middleware
|
|
18
248
|
|
|
19
|
-
Intercepts `/llms.txt` and `/llms-full.txt`
|
|
249
|
+
Intercepts `/llms.txt` and `/llms-full.txt` at the edge, passes everything else through. Use this when you need dynamic content per-request (e.g. locale from cookies, A/B testing).
|
|
20
250
|
|
|
21
251
|
```typescript
|
|
22
252
|
// middleware.ts
|
|
@@ -27,7 +257,7 @@ export default geoAIMiddleware({
|
|
|
27
257
|
siteUrl: 'https://example.com',
|
|
28
258
|
provider: new MyProvider(),
|
|
29
259
|
cache: '24h',
|
|
30
|
-
cacheMaxAge: 3600,
|
|
260
|
+
cacheMaxAge: 3600, // Cache-Control max-age in seconds (default 3600)
|
|
31
261
|
injectLinkHeader: true, // adds Link header to all responses
|
|
32
262
|
});
|
|
33
263
|
|
|
@@ -36,9 +266,11 @@ export const config = {
|
|
|
36
266
|
};
|
|
37
267
|
```
|
|
38
268
|
|
|
269
|
+
---
|
|
270
|
+
|
|
39
271
|
## Route Handler
|
|
40
272
|
|
|
41
|
-
For App Router — serves llms content at
|
|
273
|
+
For App Router — serves llms content at a custom route. File type is determined by URL path (`/llms-full.txt`) or query param `?type=full`.
|
|
42
274
|
|
|
43
275
|
```typescript
|
|
44
276
|
// app/llms/route.ts
|
|
@@ -48,15 +280,15 @@ export const { GET } = createLlmsHandler({
|
|
|
48
280
|
siteName: 'My Site',
|
|
49
281
|
siteUrl: 'https://example.com',
|
|
50
282
|
provider: new MyProvider(),
|
|
51
|
-
cacheMaxAge:
|
|
283
|
+
cacheMaxAge: 3600, // optional, default 3600
|
|
52
284
|
});
|
|
53
285
|
```
|
|
54
286
|
|
|
55
|
-
|
|
287
|
+
---
|
|
56
288
|
|
|
57
289
|
## Re-exports
|
|
58
290
|
|
|
59
|
-
All public API from `geo-ai-core` is re-exported
|
|
291
|
+
All public API from `geo-ai-core` is re-exported — no need to install both packages:
|
|
60
292
|
|
|
61
293
|
```typescript
|
|
62
294
|
import {
|
|
@@ -67,9 +299,12 @@ import {
|
|
|
67
299
|
AI_BOTS,
|
|
68
300
|
type ContentProvider,
|
|
69
301
|
type Resource,
|
|
302
|
+
type GeoAIConfig,
|
|
70
303
|
} from 'geo-ai-next';
|
|
71
304
|
```
|
|
72
305
|
|
|
306
|
+
---
|
|
307
|
+
|
|
73
308
|
## Requirements
|
|
74
309
|
|
|
75
310
|
- Node.js >= 20
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/generate.ts
|
|
13
|
+
var generate_exports = {};
|
|
14
|
+
__export(generate_exports, {
|
|
15
|
+
generateLlmsFiles: () => generateLlmsFiles
|
|
16
|
+
});
|
|
17
|
+
import { createGeoAI } from "geo-ai-core";
|
|
18
|
+
import { writeFile, mkdir } from "fs/promises";
|
|
19
|
+
import { join, resolve } from "path";
|
|
20
|
+
async function generateLlmsFiles(config) {
|
|
21
|
+
const outDir = resolve(config.outDir ?? "public");
|
|
22
|
+
console.log(`[geo-ai] Generating llms files \u2192 ${outDir}`);
|
|
23
|
+
const core = createGeoAI(config);
|
|
24
|
+
await mkdir(outDir, { recursive: true });
|
|
25
|
+
const [llmsContent, llmsFullContent] = await Promise.all([
|
|
26
|
+
core.generateLlms(false, config.locale),
|
|
27
|
+
core.generateLlms(true, config.locale)
|
|
28
|
+
]);
|
|
29
|
+
const llmsPath = join(outDir, "llms.txt");
|
|
30
|
+
const llmsFullPath = join(outDir, "llms-full.txt");
|
|
31
|
+
const tmpLlms = `${llmsPath}.tmp`;
|
|
32
|
+
const tmpFull = `${llmsFullPath}.tmp`;
|
|
33
|
+
try {
|
|
34
|
+
await Promise.all([
|
|
35
|
+
writeFile(tmpLlms, llmsContent, "utf-8"),
|
|
36
|
+
writeFile(tmpFull, llmsFullContent, "utf-8")
|
|
37
|
+
]);
|
|
38
|
+
const { rename } = await import("fs/promises");
|
|
39
|
+
await Promise.all([
|
|
40
|
+
rename(tmpLlms, llmsPath),
|
|
41
|
+
rename(tmpFull, llmsFullPath)
|
|
42
|
+
]);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
const { unlink } = await import("fs/promises");
|
|
45
|
+
await unlink(tmpLlms).catch(() => {
|
|
46
|
+
});
|
|
47
|
+
await unlink(tmpFull).catch(() => {
|
|
48
|
+
});
|
|
49
|
+
throw err;
|
|
50
|
+
}
|
|
51
|
+
console.log(`[geo-ai] \u2713 ${llmsPath} (${llmsContent.length} bytes)`);
|
|
52
|
+
console.log(`[geo-ai] \u2713 ${llmsFullPath} (${llmsFullContent.length} bytes)`);
|
|
53
|
+
return { llmsPath, llmsFullPath };
|
|
54
|
+
}
|
|
55
|
+
var init_generate = __esm({
|
|
56
|
+
"src/generate.ts"() {
|
|
57
|
+
"use strict";
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// src/cli.ts
|
|
62
|
+
import { resolve as resolve2 } from "path";
|
|
63
|
+
import { pathToFileURL } from "url";
|
|
64
|
+
async function main() {
|
|
65
|
+
const args = process.argv.slice(2);
|
|
66
|
+
let configPath = resolve2("geo-ai.config.ts");
|
|
67
|
+
const configIdx = args.indexOf("--config");
|
|
68
|
+
if (configIdx !== -1 && args[configIdx + 1]) {
|
|
69
|
+
configPath = resolve2(args[configIdx + 1]);
|
|
70
|
+
}
|
|
71
|
+
let config;
|
|
72
|
+
try {
|
|
73
|
+
const mod = await import(pathToFileURL(configPath).href);
|
|
74
|
+
config = mod.default ?? mod;
|
|
75
|
+
} catch {
|
|
76
|
+
const jsPath = configPath.replace(/\.ts$/, ".mjs");
|
|
77
|
+
try {
|
|
78
|
+
const mod = await import(pathToFileURL(jsPath).href);
|
|
79
|
+
config = mod.default ?? mod;
|
|
80
|
+
} catch {
|
|
81
|
+
console.error(`[geo-ai] Could not load config from ${configPath}`);
|
|
82
|
+
console.error(`[geo-ai] Create a geo-ai.config.ts (or .mjs) file in your project root.`);
|
|
83
|
+
console.error(`[geo-ai] Example:
|
|
84
|
+
`);
|
|
85
|
+
console.error(` import type { GenerateLlmsFilesConfig } from 'geo-ai-next';`);
|
|
86
|
+
console.error(` export default {`);
|
|
87
|
+
console.error(` siteName: 'My Site',`);
|
|
88
|
+
console.error(` siteUrl: 'https://example.com',`);
|
|
89
|
+
console.error(` provider: { Pages: [{ title: 'Home', url: '/' }] },`);
|
|
90
|
+
console.error(` } satisfies GenerateLlmsFilesConfig;
|
|
91
|
+
`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const { generateLlmsFiles: generateLlmsFiles2 } = await Promise.resolve().then(() => (init_generate(), generate_exports));
|
|
96
|
+
await generateLlmsFiles2(config);
|
|
97
|
+
console.log("[geo-ai] Done.");
|
|
98
|
+
}
|
|
99
|
+
main().catch((err) => {
|
|
100
|
+
console.error("[geo-ai] Generation failed:", err);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
});
|
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,22 +17,31 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
21
31
|
var src_exports = {};
|
|
22
32
|
__export(src_exports, {
|
|
23
|
-
AI_BOTS: () =>
|
|
24
|
-
BotRulesEngine: () =>
|
|
25
|
-
CrawlTracker: () =>
|
|
26
|
-
CryptoService: () =>
|
|
27
|
-
FileCacheAdapter: () =>
|
|
28
|
-
LlmsGenerator: () =>
|
|
29
|
-
MemoryCacheAdapter: () =>
|
|
30
|
-
MemoryCrawlStore: () =>
|
|
31
|
-
SeoGenerator: () =>
|
|
32
|
-
createGeoAI: () =>
|
|
33
|
+
AI_BOTS: () => import_geo_ai_core4.AI_BOTS,
|
|
34
|
+
BotRulesEngine: () => import_geo_ai_core4.BotRulesEngine,
|
|
35
|
+
CrawlTracker: () => import_geo_ai_core4.CrawlTracker,
|
|
36
|
+
CryptoService: () => import_geo_ai_core4.CryptoService,
|
|
37
|
+
FileCacheAdapter: () => import_geo_ai_core4.FileCacheAdapter,
|
|
38
|
+
LlmsGenerator: () => import_geo_ai_core4.LlmsGenerator,
|
|
39
|
+
MemoryCacheAdapter: () => import_geo_ai_core4.MemoryCacheAdapter,
|
|
40
|
+
MemoryCrawlStore: () => import_geo_ai_core4.MemoryCrawlStore,
|
|
41
|
+
SeoGenerator: () => import_geo_ai_core4.SeoGenerator,
|
|
42
|
+
createGeoAI: () => import_geo_ai_core4.createGeoAI,
|
|
33
43
|
createLlmsHandler: () => createLlmsHandler,
|
|
44
|
+
generateLlmsFiles: () => generateLlmsFiles,
|
|
34
45
|
geoAIMiddleware: () => geoAIMiddleware
|
|
35
46
|
});
|
|
36
47
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -102,8 +113,48 @@ function createLlmsHandler(config) {
|
|
|
102
113
|
};
|
|
103
114
|
}
|
|
104
115
|
|
|
105
|
-
// src/
|
|
116
|
+
// src/generate.ts
|
|
106
117
|
var import_geo_ai_core3 = require("geo-ai-core");
|
|
118
|
+
var import_promises = require("fs/promises");
|
|
119
|
+
var import_node_path = require("path");
|
|
120
|
+
async function generateLlmsFiles(config) {
|
|
121
|
+
const outDir = (0, import_node_path.resolve)(config.outDir ?? "public");
|
|
122
|
+
console.log(`[geo-ai] Generating llms files \u2192 ${outDir}`);
|
|
123
|
+
const core = (0, import_geo_ai_core3.createGeoAI)(config);
|
|
124
|
+
await (0, import_promises.mkdir)(outDir, { recursive: true });
|
|
125
|
+
const [llmsContent, llmsFullContent] = await Promise.all([
|
|
126
|
+
core.generateLlms(false, config.locale),
|
|
127
|
+
core.generateLlms(true, config.locale)
|
|
128
|
+
]);
|
|
129
|
+
const llmsPath = (0, import_node_path.join)(outDir, "llms.txt");
|
|
130
|
+
const llmsFullPath = (0, import_node_path.join)(outDir, "llms-full.txt");
|
|
131
|
+
const tmpLlms = `${llmsPath}.tmp`;
|
|
132
|
+
const tmpFull = `${llmsFullPath}.tmp`;
|
|
133
|
+
try {
|
|
134
|
+
await Promise.all([
|
|
135
|
+
(0, import_promises.writeFile)(tmpLlms, llmsContent, "utf-8"),
|
|
136
|
+
(0, import_promises.writeFile)(tmpFull, llmsFullContent, "utf-8")
|
|
137
|
+
]);
|
|
138
|
+
const { rename } = await import("fs/promises");
|
|
139
|
+
await Promise.all([
|
|
140
|
+
rename(tmpLlms, llmsPath),
|
|
141
|
+
rename(tmpFull, llmsFullPath)
|
|
142
|
+
]);
|
|
143
|
+
} catch (err) {
|
|
144
|
+
const { unlink } = await import("fs/promises");
|
|
145
|
+
await unlink(tmpLlms).catch(() => {
|
|
146
|
+
});
|
|
147
|
+
await unlink(tmpFull).catch(() => {
|
|
148
|
+
});
|
|
149
|
+
throw err;
|
|
150
|
+
}
|
|
151
|
+
console.log(`[geo-ai] \u2713 ${llmsPath} (${llmsContent.length} bytes)`);
|
|
152
|
+
console.log(`[geo-ai] \u2713 ${llmsFullPath} (${llmsFullContent.length} bytes)`);
|
|
153
|
+
return { llmsPath, llmsFullPath };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/index.ts
|
|
157
|
+
var import_geo_ai_core4 = require("geo-ai-core");
|
|
107
158
|
// Annotate the CommonJS export names for ESM import in node:
|
|
108
159
|
0 && (module.exports = {
|
|
109
160
|
AI_BOTS,
|
|
@@ -117,6 +168,7 @@ var import_geo_ai_core3 = require("geo-ai-core");
|
|
|
117
168
|
SeoGenerator,
|
|
118
169
|
createGeoAI,
|
|
119
170
|
createLlmsHandler,
|
|
171
|
+
generateLlmsFiles,
|
|
120
172
|
geoAIMiddleware
|
|
121
173
|
});
|
|
122
174
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/middleware.ts","../src/handler.ts"],"sourcesContent":["// geo-ai-next main entry point\n\n// Next.js middleware\nexport { geoAIMiddleware } from './middleware';\nexport type { GeoAIMiddlewareConfig } from './middleware';\n\n// Next.js route handler\nexport { createLlmsHandler } from './handler';\nexport type { LlmsHandlerConfig } from './handler';\n\n// Re-export all public types, interfaces, and classes from geo-ai-core\nexport {\n createGeoAI,\n MemoryCacheAdapter,\n FileCacheAdapter,\n CrawlTracker,\n MemoryCrawlStore,\n CryptoService,\n BotRulesEngine,\n AI_BOTS,\n SeoGenerator,\n LlmsGenerator,\n} from 'geo-ai-core';\n\nexport type {\n GeoAIInstance,\n Resource,\n ProductResource,\n ResourceSection,\n ContentProvider,\n CacheAdapter,\n CrawlEntry,\n CrawlActivity,\n CrawlStore,\n CrawlTrackingConfig,\n CryptoConfig,\n GeoAIConfig,\n AiProvider,\n AiGeneratorConfig,\n AiError,\n AiBulkConfig,\n AiContext,\n} from 'geo-ai-core';\n","import { NextResponse } from 'next/server';\nimport type { NextRequest } from 'next/server';\nimport { createGeoAI } from 'geo-ai-core';\nimport type { GeoAIConfig, GeoAIInstance } from 'geo-ai-core';\n\n// ── Config ──────────────────────────────────────────────────────────\n\nexport interface GeoAIMiddlewareConfig extends GeoAIConfig {\n /** Inject Link header pointing to llms.txt on non-llms responses. Default: false */\n injectLinkHeader?: boolean;\n /** Cache-Control max-age in seconds for llms.txt responses. Default: 3600 */\n cacheMaxAge?: number;\n}\n\n// ── Middleware factory ───────────────────────────────────────────────\n\n/**\n * Creates a Next.js middleware function that intercepts `/llms.txt` and\n * `/llms-full.txt` requests, returning generated content as `text/plain`.\n * All other paths are passed through via `NextResponse.next()`.\n */\nexport function geoAIMiddleware(\n config: GeoAIMiddlewareConfig,\n): (request: NextRequest) => Promise<NextResponse> {\n const core: GeoAIInstance = createGeoAI(config);\n const injectLink = config.injectLinkHeader ?? false;\n const maxAge = config.cacheMaxAge ?? 3600;\n\n return async (request: NextRequest): Promise<NextResponse> => {\n const { pathname } = request.nextUrl;\n\n // ── Match llms paths ───────────────────────────────────────────\n if (pathname === '/llms.txt' || pathname === '/llms-full.txt') {\n const isFull = pathname === '/llms-full.txt';\n\n try {\n const content = await core.generateLlms(isFull);\n\n // Fire-and-forget: track visit without blocking the response\n core.trackVisit(request as unknown as Request).catch(() => {});\n\n return new NextResponse(content, {\n status: 200,\n headers: {\n 'Content-Type': 'text/plain; charset=utf-8',\n 'Cache-Control': `public, max-age=${maxAge}`,\n },\n });\n } catch {\n return new NextResponse('Internal Server Error', {\n status: 500,\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\n });\n }\n }\n\n // ── Passthrough ────────────────────────────────────────────────\n const response = NextResponse.next();\n\n if (injectLink) {\n response.headers.set('Link', core.generateLinkHeader());\n }\n\n return response;\n };\n}\n","import { createGeoAI } from 'geo-ai-core';\nimport type { GeoAIConfig, GeoAIInstance } from 'geo-ai-core';\n\n// ── Config ──────────────────────────────────────────────────────────\n\nexport interface LlmsHandlerConfig extends GeoAIConfig {\n /** Cache-Control max-age in seconds for llms.txt responses. Default: 3600 */\n cacheMaxAge?: number;\n}\n\n// ── Route Handler factory ───────────────────────────────────────────\n\n/**\n * Creates a Next.js App Router route handler that serves llms.txt content.\n * File type is determined by URL path (`/llms-full.txt`) or query `?type=full`.\n *\n * Usage in `app/llms/route.ts`:\n * ```ts\n * export const { GET } = createLlmsHandler({ ... });\n * ```\n */\nexport function createLlmsHandler(config: LlmsHandlerConfig): {\n GET: (request: Request) => Promise<Response>;\n} {\n const core: GeoAIInstance = createGeoAI(config);\n const maxAge = config.cacheMaxAge ?? 3600;\n\n return {\n async GET(request: Request): Promise<Response> {\n const url = new URL(request.url);\n const isFull =\n url.pathname.endsWith('/llms-full.txt') ||\n url.searchParams.get('type') === 'full';\n\n try {\n const content = await core.generateLlms(isFull);\n\n // Fire-and-forget: log bot visit without blocking the response\n core.trackVisit(request).catch(() => {});\n\n return new Response(content, {\n status: 200,\n headers: {\n 'Content-Type': 'text/plain; charset=utf-8',\n 'Cache-Control': `public, max-age=${maxAge}`,\n },\n });\n } catch {\n return new Response('Internal Server Error', {\n status: 500,\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\n });\n }\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAA6B;AAE7B,yBAA4B;AAmBrB,SAAS,gBACd,QACiD;AACjD,QAAM,WAAsB,gCAAY,MAAM;AAC9C,QAAM,aAAa,OAAO,oBAAoB;AAC9C,QAAM,SAAS,OAAO,eAAe;AAErC,SAAO,OAAO,YAAgD;AAC5D,UAAM,EAAE,SAAS,IAAI,QAAQ;AAG7B,QAAI,aAAa,eAAe,aAAa,kBAAkB;AAC7D,YAAM,SAAS,aAAa;AAE5B,UAAI;AACF,cAAM,UAAU,MAAM,KAAK,aAAa,MAAM;AAG9C,aAAK,WAAW,OAA6B,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAE7D,eAAO,IAAI,2BAAa,SAAS;AAAA,UAC/B,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,iBAAiB,mBAAmB,MAAM;AAAA,UAC5C;AAAA,QACF,CAAC;AAAA,MACH,QAAQ;AACN,eAAO,IAAI,2BAAa,yBAAyB;AAAA,UAC/C,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,4BAA4B;AAAA,QACzD,CAAC;AAAA,MACH;AAAA,IACF;AAGA,UAAM,WAAW,2BAAa,KAAK;AAEnC,QAAI,YAAY;AACd,eAAS,QAAQ,IAAI,QAAQ,KAAK,mBAAmB,CAAC;AAAA,IACxD;AAEA,WAAO;AAAA,EACT;AACF;;;ACjEA,IAAAA,sBAA4B;AAqBrB,SAAS,kBAAkB,QAEhC;AACA,QAAM,WAAsB,iCAAY,MAAM;AAC9C,QAAM,SAAS,OAAO,eAAe;AAErC,SAAO;AAAA,IACL,MAAM,IAAI,SAAqC;AAC7C,YAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAC/B,YAAM,SACJ,IAAI,SAAS,SAAS,gBAAgB,KACtC,IAAI,aAAa,IAAI,MAAM,MAAM;AAEnC,UAAI;AACF,cAAM,UAAU,MAAM,KAAK,aAAa,MAAM;AAG9C,aAAK,WAAW,OAAO,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAEvC,eAAO,IAAI,SAAS,SAAS;AAAA,UAC3B,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,iBAAiB,mBAAmB,MAAM;AAAA,UAC5C;AAAA,QACF,CAAC;AAAA,MACH,QAAQ;AACN,eAAO,IAAI,SAAS,yBAAyB;AAAA,UAC3C,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,4BAA4B;AAAA,QACzD,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;;;AF5CA,IAAAC,sBAWO;","names":["import_geo_ai_core","import_geo_ai_core"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/middleware.ts","../src/handler.ts","../src/generate.ts"],"sourcesContent":["// geo-ai-next main entry point\n\n// Next.js middleware\nexport { geoAIMiddleware } from './middleware';\nexport type { GeoAIMiddlewareConfig } from './middleware';\n\n// Next.js route handler\nexport { createLlmsHandler } from './handler';\nexport type { LlmsHandlerConfig } from './handler';\n\n// Static file generation (build step)\nexport { generateLlmsFiles } from './generate';\nexport type { GenerateLlmsFilesConfig, GenerateLlmsFilesResult } from './generate';\n\n// Re-export all public types, interfaces, and classes from geo-ai-core\nexport {\n createGeoAI,\n MemoryCacheAdapter,\n FileCacheAdapter,\n CrawlTracker,\n MemoryCrawlStore,\n CryptoService,\n BotRulesEngine,\n AI_BOTS,\n SeoGenerator,\n LlmsGenerator,\n} from 'geo-ai-core';\n\nexport type {\n GeoAIInstance,\n Resource,\n ProductResource,\n ResourceSection,\n ContentProvider,\n CacheAdapter,\n CrawlEntry,\n CrawlActivity,\n CrawlStore,\n CrawlTrackingConfig,\n CryptoConfig,\n GeoAIConfig,\n AiProvider,\n AiGeneratorConfig,\n AiError,\n AiBulkConfig,\n AiContext,\n} from 'geo-ai-core';\n","import { NextResponse } from 'next/server';\nimport type { NextRequest } from 'next/server';\nimport { createGeoAI } from 'geo-ai-core';\nimport type { GeoAIConfig, GeoAIInstance } from 'geo-ai-core';\n\n// ── Config ──────────────────────────────────────────────────────────\n\nexport interface GeoAIMiddlewareConfig extends GeoAIConfig {\n /** Inject Link header pointing to llms.txt on non-llms responses. Default: false */\n injectLinkHeader?: boolean;\n /** Cache-Control max-age in seconds for llms.txt responses. Default: 3600 */\n cacheMaxAge?: number;\n}\n\n// ── Middleware factory ───────────────────────────────────────────────\n\n/**\n * Creates a Next.js middleware function that intercepts `/llms.txt` and\n * `/llms-full.txt` requests, returning generated content as `text/plain`.\n * All other paths are passed through via `NextResponse.next()`.\n */\nexport function geoAIMiddleware(\n config: GeoAIMiddlewareConfig,\n): (request: NextRequest) => Promise<NextResponse> {\n const core: GeoAIInstance = createGeoAI(config);\n const injectLink = config.injectLinkHeader ?? false;\n const maxAge = config.cacheMaxAge ?? 3600;\n\n return async (request: NextRequest): Promise<NextResponse> => {\n const { pathname } = request.nextUrl;\n\n // ── Match llms paths ───────────────────────────────────────────\n if (pathname === '/llms.txt' || pathname === '/llms-full.txt') {\n const isFull = pathname === '/llms-full.txt';\n\n try {\n const content = await core.generateLlms(isFull);\n\n // Fire-and-forget: track visit without blocking the response\n core.trackVisit(request as unknown as Request).catch(() => {});\n\n return new NextResponse(content, {\n status: 200,\n headers: {\n 'Content-Type': 'text/plain; charset=utf-8',\n 'Cache-Control': `public, max-age=${maxAge}`,\n },\n });\n } catch {\n return new NextResponse('Internal Server Error', {\n status: 500,\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\n });\n }\n }\n\n // ── Passthrough ────────────────────────────────────────────────\n const response = NextResponse.next();\n\n if (injectLink) {\n response.headers.set('Link', core.generateLinkHeader());\n }\n\n return response;\n };\n}\n","import { createGeoAI } from 'geo-ai-core';\nimport type { GeoAIConfig, GeoAIInstance } from 'geo-ai-core';\n\n// ── Config ──────────────────────────────────────────────────────────\n\nexport interface LlmsHandlerConfig extends GeoAIConfig {\n /** Cache-Control max-age in seconds for llms.txt responses. Default: 3600 */\n cacheMaxAge?: number;\n}\n\n// ── Route Handler factory ───────────────────────────────────────────\n\n/**\n * Creates a Next.js App Router route handler that serves llms.txt content.\n * File type is determined by URL path (`/llms-full.txt`) or query `?type=full`.\n *\n * Usage in `app/llms/route.ts`:\n * ```ts\n * export const { GET } = createLlmsHandler({ ... });\n * ```\n */\nexport function createLlmsHandler(config: LlmsHandlerConfig): {\n GET: (request: Request) => Promise<Response>;\n} {\n const core: GeoAIInstance = createGeoAI(config);\n const maxAge = config.cacheMaxAge ?? 3600;\n\n return {\n async GET(request: Request): Promise<Response> {\n const url = new URL(request.url);\n const isFull =\n url.pathname.endsWith('/llms-full.txt') ||\n url.searchParams.get('type') === 'full';\n\n try {\n const content = await core.generateLlms(isFull);\n\n // Fire-and-forget: log bot visit without blocking the response\n core.trackVisit(request).catch(() => {});\n\n return new Response(content, {\n status: 200,\n headers: {\n 'Content-Type': 'text/plain; charset=utf-8',\n 'Cache-Control': `public, max-age=${maxAge}`,\n },\n });\n } catch {\n return new Response('Internal Server Error', {\n status: 500,\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\n });\n }\n },\n };\n}\n","import { createGeoAI } from 'geo-ai-core';\nimport type { GeoAIConfig } from 'geo-ai-core';\nimport { writeFile, mkdir } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\n\n// ── Config ──────────────────────────────────────────────────────────\n\nexport interface GenerateLlmsFilesConfig extends GeoAIConfig {\n /** Output directory for generated files. Default: 'public' (relative to cwd) */\n outDir?: string;\n /** Locale for content generation */\n locale?: string;\n}\n\nexport interface GenerateLlmsFilesResult {\n llmsPath: string;\n llmsFullPath: string;\n}\n\n// ── Generator ───────────────────────────────────────────────────────\n\n/**\n * Generates `llms.txt` and `llms-full.txt` as static files in the output\n * directory (default: `public/`). Designed to be called from a build script\n * or `next.config.js` before `next build`.\n *\n * - Creates the output directory if it doesn't exist\n * - Overwrites existing files atomically (write to temp, then rename)\n * - Logs progress to stdout\n * - Throws on failure with a descriptive error\n *\n * @example\n * ```ts\n * // scripts/generate-llms.ts\n * import { generateLlmsFiles } from 'geo-ai-next';\n *\n * await generateLlmsFiles({\n * siteName: 'My Site',\n * siteUrl: 'https://example.com',\n * provider: { Pages: [{ title: 'Home', url: '/' }] },\n * });\n * ```\n */\nexport async function generateLlmsFiles(\n config: GenerateLlmsFilesConfig,\n): Promise<GenerateLlmsFilesResult> {\n const outDir = resolve(config.outDir ?? 'public');\n\n console.log(`[geo-ai] Generating llms files → ${outDir}`);\n\n const core = createGeoAI(config);\n\n // Ensure output directory exists\n await mkdir(outDir, { recursive: true });\n\n // Generate both variants\n const [llmsContent, llmsFullContent] = await Promise.all([\n core.generateLlms(false, config.locale),\n core.generateLlms(true, config.locale),\n ]);\n\n const llmsPath = join(outDir, 'llms.txt');\n const llmsFullPath = join(outDir, 'llms-full.txt');\n\n // Write files atomically: write temp then rename\n const tmpLlms = `${llmsPath}.tmp`;\n const tmpFull = `${llmsFullPath}.tmp`;\n\n try {\n await Promise.all([\n writeFile(tmpLlms, llmsContent, 'utf-8'),\n writeFile(tmpFull, llmsFullContent, 'utf-8'),\n ]);\n\n // Rename is atomic on most filesystems\n const { rename } = await import('node:fs/promises');\n await Promise.all([\n rename(tmpLlms, llmsPath),\n rename(tmpFull, llmsFullPath),\n ]);\n } catch (err) {\n // Clean up temp files on failure\n const { unlink } = await import('node:fs/promises');\n await unlink(tmpLlms).catch(() => {});\n await unlink(tmpFull).catch(() => {});\n throw err;\n }\n\n console.log(`[geo-ai] ✓ ${llmsPath} (${llmsContent.length} bytes)`);\n console.log(`[geo-ai] ✓ ${llmsFullPath} (${llmsFullContent.length} bytes)`);\n\n return { llmsPath, llmsFullPath };\n}\n"],"mappings":"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","names":["import_geo_ai_core","import_geo_ai_core","import_geo_ai_core"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -32,4 +32,38 @@ declare function createLlmsHandler(config: LlmsHandlerConfig): {
|
|
|
32
32
|
GET: (request: Request) => Promise<Response>;
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
interface GenerateLlmsFilesConfig extends GeoAIConfig {
|
|
36
|
+
/** Output directory for generated files. Default: 'public' (relative to cwd) */
|
|
37
|
+
outDir?: string;
|
|
38
|
+
/** Locale for content generation */
|
|
39
|
+
locale?: string;
|
|
40
|
+
}
|
|
41
|
+
interface GenerateLlmsFilesResult {
|
|
42
|
+
llmsPath: string;
|
|
43
|
+
llmsFullPath: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Generates `llms.txt` and `llms-full.txt` as static files in the output
|
|
47
|
+
* directory (default: `public/`). Designed to be called from a build script
|
|
48
|
+
* or `next.config.js` before `next build`.
|
|
49
|
+
*
|
|
50
|
+
* - Creates the output directory if it doesn't exist
|
|
51
|
+
* - Overwrites existing files atomically (write to temp, then rename)
|
|
52
|
+
* - Logs progress to stdout
|
|
53
|
+
* - Throws on failure with a descriptive error
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* // scripts/generate-llms.ts
|
|
58
|
+
* import { generateLlmsFiles } from 'geo-ai-next';
|
|
59
|
+
*
|
|
60
|
+
* await generateLlmsFiles({
|
|
61
|
+
* siteName: 'My Site',
|
|
62
|
+
* siteUrl: 'https://example.com',
|
|
63
|
+
* provider: { Pages: [{ title: 'Home', url: '/' }] },
|
|
64
|
+
* });
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
declare function generateLlmsFiles(config: GenerateLlmsFilesConfig): Promise<GenerateLlmsFilesResult>;
|
|
68
|
+
|
|
69
|
+
export { type GenerateLlmsFilesConfig, type GenerateLlmsFilesResult, type GeoAIMiddlewareConfig, type LlmsHandlerConfig, createLlmsHandler, generateLlmsFiles, geoAIMiddleware };
|
package/dist/index.d.ts
CHANGED
|
@@ -32,4 +32,38 @@ declare function createLlmsHandler(config: LlmsHandlerConfig): {
|
|
|
32
32
|
GET: (request: Request) => Promise<Response>;
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
interface GenerateLlmsFilesConfig extends GeoAIConfig {
|
|
36
|
+
/** Output directory for generated files. Default: 'public' (relative to cwd) */
|
|
37
|
+
outDir?: string;
|
|
38
|
+
/** Locale for content generation */
|
|
39
|
+
locale?: string;
|
|
40
|
+
}
|
|
41
|
+
interface GenerateLlmsFilesResult {
|
|
42
|
+
llmsPath: string;
|
|
43
|
+
llmsFullPath: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Generates `llms.txt` and `llms-full.txt` as static files in the output
|
|
47
|
+
* directory (default: `public/`). Designed to be called from a build script
|
|
48
|
+
* or `next.config.js` before `next build`.
|
|
49
|
+
*
|
|
50
|
+
* - Creates the output directory if it doesn't exist
|
|
51
|
+
* - Overwrites existing files atomically (write to temp, then rename)
|
|
52
|
+
* - Logs progress to stdout
|
|
53
|
+
* - Throws on failure with a descriptive error
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* // scripts/generate-llms.ts
|
|
58
|
+
* import { generateLlmsFiles } from 'geo-ai-next';
|
|
59
|
+
*
|
|
60
|
+
* await generateLlmsFiles({
|
|
61
|
+
* siteName: 'My Site',
|
|
62
|
+
* siteUrl: 'https://example.com',
|
|
63
|
+
* provider: { Pages: [{ title: 'Home', url: '/' }] },
|
|
64
|
+
* });
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
declare function generateLlmsFiles(config: GenerateLlmsFilesConfig): Promise<GenerateLlmsFilesResult>;
|
|
68
|
+
|
|
69
|
+
export { type GenerateLlmsFilesConfig, type GenerateLlmsFilesResult, type GeoAIMiddlewareConfig, type LlmsHandlerConfig, createLlmsHandler, generateLlmsFiles, geoAIMiddleware };
|
package/dist/index.mjs
CHANGED
|
@@ -65,9 +65,49 @@ function createLlmsHandler(config) {
|
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
// src/generate.ts
|
|
69
|
+
import { createGeoAI as createGeoAI3 } from "geo-ai-core";
|
|
70
|
+
import { writeFile, mkdir } from "fs/promises";
|
|
71
|
+
import { join, resolve } from "path";
|
|
72
|
+
async function generateLlmsFiles(config) {
|
|
73
|
+
const outDir = resolve(config.outDir ?? "public");
|
|
74
|
+
console.log(`[geo-ai] Generating llms files \u2192 ${outDir}`);
|
|
75
|
+
const core = createGeoAI3(config);
|
|
76
|
+
await mkdir(outDir, { recursive: true });
|
|
77
|
+
const [llmsContent, llmsFullContent] = await Promise.all([
|
|
78
|
+
core.generateLlms(false, config.locale),
|
|
79
|
+
core.generateLlms(true, config.locale)
|
|
80
|
+
]);
|
|
81
|
+
const llmsPath = join(outDir, "llms.txt");
|
|
82
|
+
const llmsFullPath = join(outDir, "llms-full.txt");
|
|
83
|
+
const tmpLlms = `${llmsPath}.tmp`;
|
|
84
|
+
const tmpFull = `${llmsFullPath}.tmp`;
|
|
85
|
+
try {
|
|
86
|
+
await Promise.all([
|
|
87
|
+
writeFile(tmpLlms, llmsContent, "utf-8"),
|
|
88
|
+
writeFile(tmpFull, llmsFullContent, "utf-8")
|
|
89
|
+
]);
|
|
90
|
+
const { rename } = await import("fs/promises");
|
|
91
|
+
await Promise.all([
|
|
92
|
+
rename(tmpLlms, llmsPath),
|
|
93
|
+
rename(tmpFull, llmsFullPath)
|
|
94
|
+
]);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
const { unlink } = await import("fs/promises");
|
|
97
|
+
await unlink(tmpLlms).catch(() => {
|
|
98
|
+
});
|
|
99
|
+
await unlink(tmpFull).catch(() => {
|
|
100
|
+
});
|
|
101
|
+
throw err;
|
|
102
|
+
}
|
|
103
|
+
console.log(`[geo-ai] \u2713 ${llmsPath} (${llmsContent.length} bytes)`);
|
|
104
|
+
console.log(`[geo-ai] \u2713 ${llmsFullPath} (${llmsFullContent.length} bytes)`);
|
|
105
|
+
return { llmsPath, llmsFullPath };
|
|
106
|
+
}
|
|
107
|
+
|
|
68
108
|
// src/index.ts
|
|
69
109
|
import {
|
|
70
|
-
createGeoAI as
|
|
110
|
+
createGeoAI as createGeoAI4,
|
|
71
111
|
MemoryCacheAdapter,
|
|
72
112
|
FileCacheAdapter,
|
|
73
113
|
CrawlTracker,
|
|
@@ -88,8 +128,9 @@ export {
|
|
|
88
128
|
MemoryCacheAdapter,
|
|
89
129
|
MemoryCrawlStore,
|
|
90
130
|
SeoGenerator,
|
|
91
|
-
|
|
131
|
+
createGeoAI4 as createGeoAI,
|
|
92
132
|
createLlmsHandler,
|
|
133
|
+
generateLlmsFiles,
|
|
93
134
|
geoAIMiddleware
|
|
94
135
|
};
|
|
95
136
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/middleware.ts","../src/handler.ts","../src/index.ts"],"sourcesContent":["import { NextResponse } from 'next/server';\nimport type { NextRequest } from 'next/server';\nimport { createGeoAI } from 'geo-ai-core';\nimport type { GeoAIConfig, GeoAIInstance } from 'geo-ai-core';\n\n// ── Config ──────────────────────────────────────────────────────────\n\nexport interface GeoAIMiddlewareConfig extends GeoAIConfig {\n /** Inject Link header pointing to llms.txt on non-llms responses. Default: false */\n injectLinkHeader?: boolean;\n /** Cache-Control max-age in seconds for llms.txt responses. Default: 3600 */\n cacheMaxAge?: number;\n}\n\n// ── Middleware factory ───────────────────────────────────────────────\n\n/**\n * Creates a Next.js middleware function that intercepts `/llms.txt` and\n * `/llms-full.txt` requests, returning generated content as `text/plain`.\n * All other paths are passed through via `NextResponse.next()`.\n */\nexport function geoAIMiddleware(\n config: GeoAIMiddlewareConfig,\n): (request: NextRequest) => Promise<NextResponse> {\n const core: GeoAIInstance = createGeoAI(config);\n const injectLink = config.injectLinkHeader ?? false;\n const maxAge = config.cacheMaxAge ?? 3600;\n\n return async (request: NextRequest): Promise<NextResponse> => {\n const { pathname } = request.nextUrl;\n\n // ── Match llms paths ───────────────────────────────────────────\n if (pathname === '/llms.txt' || pathname === '/llms-full.txt') {\n const isFull = pathname === '/llms-full.txt';\n\n try {\n const content = await core.generateLlms(isFull);\n\n // Fire-and-forget: track visit without blocking the response\n core.trackVisit(request as unknown as Request).catch(() => {});\n\n return new NextResponse(content, {\n status: 200,\n headers: {\n 'Content-Type': 'text/plain; charset=utf-8',\n 'Cache-Control': `public, max-age=${maxAge}`,\n },\n });\n } catch {\n return new NextResponse('Internal Server Error', {\n status: 500,\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\n });\n }\n }\n\n // ── Passthrough ────────────────────────────────────────────────\n const response = NextResponse.next();\n\n if (injectLink) {\n response.headers.set('Link', core.generateLinkHeader());\n }\n\n return response;\n };\n}\n","import { createGeoAI } from 'geo-ai-core';\nimport type { GeoAIConfig, GeoAIInstance } from 'geo-ai-core';\n\n// ── Config ──────────────────────────────────────────────────────────\n\nexport interface LlmsHandlerConfig extends GeoAIConfig {\n /** Cache-Control max-age in seconds for llms.txt responses. Default: 3600 */\n cacheMaxAge?: number;\n}\n\n// ── Route Handler factory ───────────────────────────────────────────\n\n/**\n * Creates a Next.js App Router route handler that serves llms.txt content.\n * File type is determined by URL path (`/llms-full.txt`) or query `?type=full`.\n *\n * Usage in `app/llms/route.ts`:\n * ```ts\n * export const { GET } = createLlmsHandler({ ... });\n * ```\n */\nexport function createLlmsHandler(config: LlmsHandlerConfig): {\n GET: (request: Request) => Promise<Response>;\n} {\n const core: GeoAIInstance = createGeoAI(config);\n const maxAge = config.cacheMaxAge ?? 3600;\n\n return {\n async GET(request: Request): Promise<Response> {\n const url = new URL(request.url);\n const isFull =\n url.pathname.endsWith('/llms-full.txt') ||\n url.searchParams.get('type') === 'full';\n\n try {\n const content = await core.generateLlms(isFull);\n\n // Fire-and-forget: log bot visit without blocking the response\n core.trackVisit(request).catch(() => {});\n\n return new Response(content, {\n status: 200,\n headers: {\n 'Content-Type': 'text/plain; charset=utf-8',\n 'Cache-Control': `public, max-age=${maxAge}`,\n },\n });\n } catch {\n return new Response('Internal Server Error', {\n status: 500,\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\n });\n }\n },\n };\n}\n","// geo-ai-next main entry point\n\n// Next.js middleware\nexport { geoAIMiddleware } from './middleware';\nexport type { GeoAIMiddlewareConfig } from './middleware';\n\n// Next.js route handler\nexport { createLlmsHandler } from './handler';\nexport type { LlmsHandlerConfig } from './handler';\n\n// Re-export all public types, interfaces, and classes from geo-ai-core\nexport {\n createGeoAI,\n MemoryCacheAdapter,\n FileCacheAdapter,\n CrawlTracker,\n MemoryCrawlStore,\n CryptoService,\n BotRulesEngine,\n AI_BOTS,\n SeoGenerator,\n LlmsGenerator,\n} from 'geo-ai-core';\n\nexport type {\n GeoAIInstance,\n Resource,\n ProductResource,\n ResourceSection,\n ContentProvider,\n CacheAdapter,\n CrawlEntry,\n CrawlActivity,\n CrawlStore,\n CrawlTrackingConfig,\n CryptoConfig,\n GeoAIConfig,\n AiProvider,\n AiGeneratorConfig,\n AiError,\n AiBulkConfig,\n AiContext,\n} from 'geo-ai-core';\n"],"mappings":";AAAA,SAAS,oBAAoB;AAE7B,SAAS,mBAAmB;AAmBrB,SAAS,gBACd,QACiD;AACjD,QAAM,OAAsB,YAAY,MAAM;AAC9C,QAAM,aAAa,OAAO,oBAAoB;AAC9C,QAAM,SAAS,OAAO,eAAe;AAErC,SAAO,OAAO,YAAgD;AAC5D,UAAM,EAAE,SAAS,IAAI,QAAQ;AAG7B,QAAI,aAAa,eAAe,aAAa,kBAAkB;AAC7D,YAAM,SAAS,aAAa;AAE5B,UAAI;AACF,cAAM,UAAU,MAAM,KAAK,aAAa,MAAM;AAG9C,aAAK,WAAW,OAA6B,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAE7D,eAAO,IAAI,aAAa,SAAS;AAAA,UAC/B,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,iBAAiB,mBAAmB,MAAM;AAAA,UAC5C;AAAA,QACF,CAAC;AAAA,MACH,QAAQ;AACN,eAAO,IAAI,aAAa,yBAAyB;AAAA,UAC/C,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,4BAA4B;AAAA,QACzD,CAAC;AAAA,MACH;AAAA,IACF;AAGA,UAAM,WAAW,aAAa,KAAK;AAEnC,QAAI,YAAY;AACd,eAAS,QAAQ,IAAI,QAAQ,KAAK,mBAAmB,CAAC;AAAA,IACxD;AAEA,WAAO;AAAA,EACT;AACF;;;ACjEA,SAAS,eAAAA,oBAAmB;AAqBrB,SAAS,kBAAkB,QAEhC;AACA,QAAM,OAAsBA,aAAY,MAAM;AAC9C,QAAM,SAAS,OAAO,eAAe;AAErC,SAAO;AAAA,IACL,MAAM,IAAI,SAAqC;AAC7C,YAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAC/B,YAAM,SACJ,IAAI,SAAS,SAAS,gBAAgB,KACtC,IAAI,aAAa,IAAI,MAAM,MAAM;AAEnC,UAAI;AACF,cAAM,UAAU,MAAM,KAAK,aAAa,MAAM;AAG9C,aAAK,WAAW,OAAO,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAEvC,eAAO,IAAI,SAAS,SAAS;AAAA,UAC3B,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,iBAAiB,mBAAmB,MAAM;AAAA,UAC5C;AAAA,QACF,CAAC;AAAA,MACH,QAAQ;AACN,eAAO,IAAI,SAAS,yBAAyB;AAAA,UAC3C,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,4BAA4B;AAAA,QACzD,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;;;AC5CA;AAAA,EACE,eAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;","names":["createGeoAI","createGeoAI"]}
|
|
1
|
+
{"version":3,"sources":["../src/middleware.ts","../src/handler.ts","../src/generate.ts","../src/index.ts"],"sourcesContent":["import { NextResponse } from 'next/server';\nimport type { NextRequest } from 'next/server';\nimport { createGeoAI } from 'geo-ai-core';\nimport type { GeoAIConfig, GeoAIInstance } from 'geo-ai-core';\n\n// ── Config ──────────────────────────────────────────────────────────\n\nexport interface GeoAIMiddlewareConfig extends GeoAIConfig {\n /** Inject Link header pointing to llms.txt on non-llms responses. Default: false */\n injectLinkHeader?: boolean;\n /** Cache-Control max-age in seconds for llms.txt responses. Default: 3600 */\n cacheMaxAge?: number;\n}\n\n// ── Middleware factory ───────────────────────────────────────────────\n\n/**\n * Creates a Next.js middleware function that intercepts `/llms.txt` and\n * `/llms-full.txt` requests, returning generated content as `text/plain`.\n * All other paths are passed through via `NextResponse.next()`.\n */\nexport function geoAIMiddleware(\n config: GeoAIMiddlewareConfig,\n): (request: NextRequest) => Promise<NextResponse> {\n const core: GeoAIInstance = createGeoAI(config);\n const injectLink = config.injectLinkHeader ?? false;\n const maxAge = config.cacheMaxAge ?? 3600;\n\n return async (request: NextRequest): Promise<NextResponse> => {\n const { pathname } = request.nextUrl;\n\n // ── Match llms paths ───────────────────────────────────────────\n if (pathname === '/llms.txt' || pathname === '/llms-full.txt') {\n const isFull = pathname === '/llms-full.txt';\n\n try {\n const content = await core.generateLlms(isFull);\n\n // Fire-and-forget: track visit without blocking the response\n core.trackVisit(request as unknown as Request).catch(() => {});\n\n return new NextResponse(content, {\n status: 200,\n headers: {\n 'Content-Type': 'text/plain; charset=utf-8',\n 'Cache-Control': `public, max-age=${maxAge}`,\n },\n });\n } catch {\n return new NextResponse('Internal Server Error', {\n status: 500,\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\n });\n }\n }\n\n // ── Passthrough ────────────────────────────────────────────────\n const response = NextResponse.next();\n\n if (injectLink) {\n response.headers.set('Link', core.generateLinkHeader());\n }\n\n return response;\n };\n}\n","import { createGeoAI } from 'geo-ai-core';\nimport type { GeoAIConfig, GeoAIInstance } from 'geo-ai-core';\n\n// ── Config ──────────────────────────────────────────────────────────\n\nexport interface LlmsHandlerConfig extends GeoAIConfig {\n /** Cache-Control max-age in seconds for llms.txt responses. Default: 3600 */\n cacheMaxAge?: number;\n}\n\n// ── Route Handler factory ───────────────────────────────────────────\n\n/**\n * Creates a Next.js App Router route handler that serves llms.txt content.\n * File type is determined by URL path (`/llms-full.txt`) or query `?type=full`.\n *\n * Usage in `app/llms/route.ts`:\n * ```ts\n * export const { GET } = createLlmsHandler({ ... });\n * ```\n */\nexport function createLlmsHandler(config: LlmsHandlerConfig): {\n GET: (request: Request) => Promise<Response>;\n} {\n const core: GeoAIInstance = createGeoAI(config);\n const maxAge = config.cacheMaxAge ?? 3600;\n\n return {\n async GET(request: Request): Promise<Response> {\n const url = new URL(request.url);\n const isFull =\n url.pathname.endsWith('/llms-full.txt') ||\n url.searchParams.get('type') === 'full';\n\n try {\n const content = await core.generateLlms(isFull);\n\n // Fire-and-forget: log bot visit without blocking the response\n core.trackVisit(request).catch(() => {});\n\n return new Response(content, {\n status: 200,\n headers: {\n 'Content-Type': 'text/plain; charset=utf-8',\n 'Cache-Control': `public, max-age=${maxAge}`,\n },\n });\n } catch {\n return new Response('Internal Server Error', {\n status: 500,\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\n });\n }\n },\n };\n}\n","import { createGeoAI } from 'geo-ai-core';\nimport type { GeoAIConfig } from 'geo-ai-core';\nimport { writeFile, mkdir } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\n\n// ── Config ──────────────────────────────────────────────────────────\n\nexport interface GenerateLlmsFilesConfig extends GeoAIConfig {\n /** Output directory for generated files. Default: 'public' (relative to cwd) */\n outDir?: string;\n /** Locale for content generation */\n locale?: string;\n}\n\nexport interface GenerateLlmsFilesResult {\n llmsPath: string;\n llmsFullPath: string;\n}\n\n// ── Generator ───────────────────────────────────────────────────────\n\n/**\n * Generates `llms.txt` and `llms-full.txt` as static files in the output\n * directory (default: `public/`). Designed to be called from a build script\n * or `next.config.js` before `next build`.\n *\n * - Creates the output directory if it doesn't exist\n * - Overwrites existing files atomically (write to temp, then rename)\n * - Logs progress to stdout\n * - Throws on failure with a descriptive error\n *\n * @example\n * ```ts\n * // scripts/generate-llms.ts\n * import { generateLlmsFiles } from 'geo-ai-next';\n *\n * await generateLlmsFiles({\n * siteName: 'My Site',\n * siteUrl: 'https://example.com',\n * provider: { Pages: [{ title: 'Home', url: '/' }] },\n * });\n * ```\n */\nexport async function generateLlmsFiles(\n config: GenerateLlmsFilesConfig,\n): Promise<GenerateLlmsFilesResult> {\n const outDir = resolve(config.outDir ?? 'public');\n\n console.log(`[geo-ai] Generating llms files → ${outDir}`);\n\n const core = createGeoAI(config);\n\n // Ensure output directory exists\n await mkdir(outDir, { recursive: true });\n\n // Generate both variants\n const [llmsContent, llmsFullContent] = await Promise.all([\n core.generateLlms(false, config.locale),\n core.generateLlms(true, config.locale),\n ]);\n\n const llmsPath = join(outDir, 'llms.txt');\n const llmsFullPath = join(outDir, 'llms-full.txt');\n\n // Write files atomically: write temp then rename\n const tmpLlms = `${llmsPath}.tmp`;\n const tmpFull = `${llmsFullPath}.tmp`;\n\n try {\n await Promise.all([\n writeFile(tmpLlms, llmsContent, 'utf-8'),\n writeFile(tmpFull, llmsFullContent, 'utf-8'),\n ]);\n\n // Rename is atomic on most filesystems\n const { rename } = await import('node:fs/promises');\n await Promise.all([\n rename(tmpLlms, llmsPath),\n rename(tmpFull, llmsFullPath),\n ]);\n } catch (err) {\n // Clean up temp files on failure\n const { unlink } = await import('node:fs/promises');\n await unlink(tmpLlms).catch(() => {});\n await unlink(tmpFull).catch(() => {});\n throw err;\n }\n\n console.log(`[geo-ai] ✓ ${llmsPath} (${llmsContent.length} bytes)`);\n console.log(`[geo-ai] ✓ ${llmsFullPath} (${llmsFullContent.length} bytes)`);\n\n return { llmsPath, llmsFullPath };\n}\n","// geo-ai-next main entry point\n\n// Next.js middleware\nexport { geoAIMiddleware } from './middleware';\nexport type { GeoAIMiddlewareConfig } from './middleware';\n\n// Next.js route handler\nexport { createLlmsHandler } from './handler';\nexport type { LlmsHandlerConfig } from './handler';\n\n// Static file generation (build step)\nexport { generateLlmsFiles } from './generate';\nexport type { GenerateLlmsFilesConfig, GenerateLlmsFilesResult } from './generate';\n\n// Re-export all public types, interfaces, and classes from geo-ai-core\nexport {\n createGeoAI,\n MemoryCacheAdapter,\n FileCacheAdapter,\n CrawlTracker,\n MemoryCrawlStore,\n CryptoService,\n BotRulesEngine,\n AI_BOTS,\n SeoGenerator,\n LlmsGenerator,\n} from 'geo-ai-core';\n\nexport type {\n GeoAIInstance,\n Resource,\n ProductResource,\n ResourceSection,\n ContentProvider,\n CacheAdapter,\n CrawlEntry,\n CrawlActivity,\n CrawlStore,\n CrawlTrackingConfig,\n CryptoConfig,\n GeoAIConfig,\n AiProvider,\n AiGeneratorConfig,\n AiError,\n AiBulkConfig,\n AiContext,\n} from 'geo-ai-core';\n"],"mappings":"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","names":["createGeoAI","createGeoAI","createGeoAI"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "geo-ai-next",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"license": "GPL-2.0",
|
|
5
|
+
"homepage": "https://www.geoai.run",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/madeburo/GEO-AI.git",
|
|
9
|
+
"directory": "packages/next"
|
|
10
|
+
},
|
|
5
11
|
"type": "module",
|
|
6
12
|
"exports": {
|
|
7
13
|
".": {
|
|
@@ -19,7 +25,8 @@
|
|
|
19
25
|
"llms-txt",
|
|
20
26
|
"llms.txt",
|
|
21
27
|
"ai",
|
|
22
|
-
"
|
|
28
|
+
"ai-search-optimization",
|
|
29
|
+
"geoai",
|
|
23
30
|
"nextjs",
|
|
24
31
|
"next",
|
|
25
32
|
"middleware",
|
|
@@ -38,12 +45,15 @@
|
|
|
38
45
|
"files": [
|
|
39
46
|
"dist"
|
|
40
47
|
],
|
|
48
|
+
"bin": {
|
|
49
|
+
"geo-ai-generate": "dist/cli.mjs"
|
|
50
|
+
},
|
|
41
51
|
"scripts": {
|
|
42
52
|
"build": "tsup",
|
|
43
53
|
"typecheck": "tsc --noEmit"
|
|
44
54
|
},
|
|
45
55
|
"dependencies": {
|
|
46
|
-
"geo-ai-core": "
|
|
56
|
+
"geo-ai-core": "^0.2.0"
|
|
47
57
|
},
|
|
48
58
|
"engines": {
|
|
49
59
|
"node": ">=20"
|