next-ai-discovery 0.1.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/LICENSE +22 -0
- package/README.md +267 -0
- package/dist/index.cjs +21 -0
- package/dist/index.d.cts +11 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/llms-route.cjs +37 -0
- package/dist/llms-route.d.cts +13 -0
- package/dist/llms-route.d.cts.map +1 -0
- package/dist/llms-route.d.ts +13 -0
- package/dist/llms-route.d.ts.map +1 -0
- package/dist/llms-route.js +33 -0
- package/dist/llms.cjs +40 -0
- package/dist/llms.d.cts +20 -0
- package/dist/llms.d.cts.map +1 -0
- package/dist/llms.d.ts +20 -0
- package/dist/llms.d.ts.map +1 -0
- package/dist/llms.js +36 -0
- package/dist/markdown-route.cjs +59 -0
- package/dist/markdown-route.d.cts +16 -0
- package/dist/markdown-route.d.cts.map +1 -0
- package/dist/markdown-route.d.ts +16 -0
- package/dist/markdown-route.d.ts.map +1 -0
- package/dist/markdown-route.js +55 -0
- package/dist/metadata.cjs +26 -0
- package/dist/metadata.d.cts +57 -0
- package/dist/metadata.d.cts.map +1 -0
- package/dist/metadata.d.ts +57 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +22 -0
- package/dist/pathname.cjs +37 -0
- package/dist/pathname.d.cts +4 -0
- package/dist/pathname.d.cts.map +1 -0
- package/dist/pathname.d.ts +4 -0
- package/dist/pathname.d.ts.map +1 -0
- package/dist/pathname.js +32 -0
- package/dist/proxy.cjs +79 -0
- package/dist/proxy.d.cts +22 -0
- package/dist/proxy.d.cts.map +1 -0
- package/dist/proxy.d.ts +22 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +75 -0
- package/package.json +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
The MIT License (MIT)
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2025 Nivalis
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# next-ai-discovery
|
|
2
|
+
|
|
3
|
+
Next.js 16 (App Router) helpers to make your site AI-discoverable by serving Markdown variants and advertising them via standard `<link rel="alternate" type="text/markdown" ...>` tags.
|
|
4
|
+
|
|
5
|
+
What this package provides:
|
|
6
|
+
|
|
7
|
+
- A `proxy.ts` factory to rewrite Markdown requests to a single internal route handler
|
|
8
|
+
- A route handler factory to serve per-page Markdown with correct headers (`Content-Type`, `Vary: Accept`)
|
|
9
|
+
- Metadata helpers to advertise per-page Markdown alternates
|
|
10
|
+
- A `llms.txt` route handler factory (and optional `llms-full.txt`)
|
|
11
|
+
|
|
12
|
+
## Core behavior (explicit contract)
|
|
13
|
+
|
|
14
|
+
- Only these requests are rewritten to the internal Markdown endpoint:
|
|
15
|
+
- URLs ending in `.md` (example: `/docs.md`)
|
|
16
|
+
- Requests whose `Accept` header contains `text/markdown` (example: `Accept: text/markdown, text/html;q=0.9`)
|
|
17
|
+
- Other dot-paths are never rewritten (assets). Example: `/logo.png` is not rewritten.
|
|
18
|
+
- Root mapping is intentional: `/` is normalized to `/index`, so the home page Markdown twin is `/index.md`.
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm i next-ai-discovery
|
|
24
|
+
# or
|
|
25
|
+
pnpm add next-ai-discovery
|
|
26
|
+
# or
|
|
27
|
+
bun add next-ai-discovery
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Minimal setup (Next.js 16 App Router)
|
|
31
|
+
|
|
32
|
+
### 1) Add `proxy.ts`
|
|
33
|
+
|
|
34
|
+
Create `proxy.ts` at the project root:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { createMarkdownProxy } from 'next-ai-discovery';
|
|
38
|
+
|
|
39
|
+
export default createMarkdownProxy();
|
|
40
|
+
|
|
41
|
+
export const config = {
|
|
42
|
+
// Recommended matcher:
|
|
43
|
+
// - runs on "normal" routes (no dot)
|
|
44
|
+
// - also runs on explicit `.md` routes
|
|
45
|
+
matcher: ['/((?!_next/|api/|.*\\..*).*)', '/(.*\\.md)'],
|
|
46
|
+
};
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Notes:
|
|
50
|
+
|
|
51
|
+
- Your `matcher` controls where Next runs the proxy. The proxy itself still has its own rewrite rules.
|
|
52
|
+
- Internal endpoint default is `DEFAULT_ENDPOINT_PATH = '/__aid/md'`.
|
|
53
|
+
- The internal endpoint is automatically excluded to avoid rewrite loops.
|
|
54
|
+
|
|
55
|
+
### 2) Add the internal Markdown endpoint
|
|
56
|
+
|
|
57
|
+
Create `app/__aid/md/route.ts`:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { createMarkdownRoute } from 'next-ai-discovery';
|
|
61
|
+
import type { NextRequest } from 'next/server';
|
|
62
|
+
|
|
63
|
+
const handler = createMarkdownRoute({
|
|
64
|
+
async getMarkdown(pathname, request: NextRequest) {
|
|
65
|
+
// IMPORTANT: `getMarkdown()` is your policy boundary.
|
|
66
|
+
// Enforce the same auth/policy as your HTML routes.
|
|
67
|
+
|
|
68
|
+
// Root is normalized to `/index`.
|
|
69
|
+
if (pathname === '/index') {
|
|
70
|
+
return {
|
|
71
|
+
frontmatter: {
|
|
72
|
+
title: 'home',
|
|
73
|
+
canonical: 'https://example.com/',
|
|
74
|
+
},
|
|
75
|
+
body: '# home\n\nhello.',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return null;
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
export const GET = handler;
|
|
84
|
+
export const HEAD = handler;
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Notes:
|
|
88
|
+
|
|
89
|
+
- Responses include `Content-Type: text/markdown; charset=utf-8` and `Vary: Accept`.
|
|
90
|
+
- `HEAD` is supported; the handler returns the same status/headers but no body.
|
|
91
|
+
|
|
92
|
+
### 3) Add `llms.txt`
|
|
93
|
+
|
|
94
|
+
`/llms.txt` is a proposed convention for publishing a short, curated Markdown index to help LLMs and agents understand your site.
|
|
95
|
+
Reference: `https://llmstxt.org/`
|
|
96
|
+
|
|
97
|
+
Create `app/llms.txt/route.ts`:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import { createLlmsTxtRoute } from 'next-ai-discovery';
|
|
101
|
+
|
|
102
|
+
export const GET = createLlmsTxtRoute({
|
|
103
|
+
config: {
|
|
104
|
+
site: {
|
|
105
|
+
name: 'Example.com',
|
|
106
|
+
description: 'This site publishes articles about X.',
|
|
107
|
+
url: 'https://example.com',
|
|
108
|
+
},
|
|
109
|
+
sections: [{ title: 'Key sections', items: ['/blog', '/docs', '/about'] }],
|
|
110
|
+
markdown: {
|
|
111
|
+
appendDotMd: true,
|
|
112
|
+
acceptNegotiation: true,
|
|
113
|
+
fullIndexPath: '/llms-full.txt',
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Optional full variant:
|
|
120
|
+
|
|
121
|
+
- Create `app/llms-full.txt/route.ts` with `variant: 'full'`.
|
|
122
|
+
- “full” is not auto-generated by this package; it just selects a variant so you can return a larger inventory if you want.
|
|
123
|
+
|
|
124
|
+
Advertising `llms.txt`:
|
|
125
|
+
|
|
126
|
+
- This package does not auto-inject a global `<link ... href="/llms.txt">`.
|
|
127
|
+
- If you want it, add it in your root `app/layout.tsx` metadata manually.
|
|
128
|
+
|
|
129
|
+
## What crawlers will see (HTTP examples)
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# content negotiation
|
|
133
|
+
curl -i -H 'Accept: text/markdown' https://example.com/docs
|
|
134
|
+
|
|
135
|
+
# explicit .md
|
|
136
|
+
curl -i https://example.com/docs.md
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Expected headers (both):
|
|
140
|
+
|
|
141
|
+
- `Content-Type: text/markdown; charset=utf-8`
|
|
142
|
+
- `Vary: Accept`
|
|
143
|
+
|
|
144
|
+
## Per-page Markdown auto-discovery
|
|
145
|
+
|
|
146
|
+
To advertise a Markdown twin from HTML using the Next.js Metadata API, use `withMarkdownAlternate()`.
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
import { withMarkdownAlternate } from 'next-ai-discovery';
|
|
150
|
+
import type { Metadata } from 'next';
|
|
151
|
+
|
|
152
|
+
export async function generateMetadata(): Promise<Metadata> {
|
|
153
|
+
return withMarkdownAlternate({ title: 'Docs' }, '/docs');
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
This emits:
|
|
158
|
+
|
|
159
|
+
```html
|
|
160
|
+
<link rel="alternate" type="text/markdown" href="/docs.md" />
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Home page note:
|
|
164
|
+
|
|
165
|
+
- `pathnameToMd('/')` yields `/index.md` (because `/` is normalized to `/index`).
|
|
166
|
+
|
|
167
|
+
## Configuration
|
|
168
|
+
|
|
169
|
+
### `createMarkdownProxy(options)`
|
|
170
|
+
|
|
171
|
+
- `endpointPath` (default: `/__aid/md`)
|
|
172
|
+
- `enableDotMd` (default: `true`)
|
|
173
|
+
- `enableAcceptNegotiation` (default: `true`)
|
|
174
|
+
- `acceptHeader` (default: `text/markdown`)
|
|
175
|
+
- `exclude(pathname): boolean` (optional)
|
|
176
|
+
- `excludePrefixes` (default: `["/_next", "/api"]`)
|
|
177
|
+
- `excludeExact` (default: `["/robots.txt", "/sitemap.xml"]`)
|
|
178
|
+
- `onRewrite({ type: 'accept' | 'dotmd', pathname })` (optional)
|
|
179
|
+
|
|
180
|
+
Precedence / terminology:
|
|
181
|
+
|
|
182
|
+
- `config.matcher` decides which requests execute the proxy at all.
|
|
183
|
+
- `exclude*` decides which requests the proxy will rewrite.
|
|
184
|
+
|
|
185
|
+
Content negotiation semantics:
|
|
186
|
+
|
|
187
|
+
- Any `Accept` header value that contains a comma-separated entry starting with `text/markdown` triggers Markdown (q-values are ignored).
|
|
188
|
+
|
|
189
|
+
Dot-path behavior:
|
|
190
|
+
|
|
191
|
+
- For `Accept` negotiation, the proxy will not rewrite “asset-like” URLs containing a dot after the last slash (example: `/logo.png`).
|
|
192
|
+
- Explicit `.md` URLs are always eligible (example: `/docs.md`).
|
|
193
|
+
|
|
194
|
+
### `createMarkdownRoute({ getMarkdown, includeFrontmatter, onServed })`
|
|
195
|
+
|
|
196
|
+
- `getMarkdown(pathname, request)` returns `{ body, frontmatter? }` or `null`
|
|
197
|
+
- `includeFrontmatter` (default: `true`)
|
|
198
|
+
- `onServed({ pathname, status })` (optional)
|
|
199
|
+
|
|
200
|
+
### Path normalization
|
|
201
|
+
|
|
202
|
+
Internally, paths are normalized to make matching predictable:
|
|
203
|
+
|
|
204
|
+
- `/` -> `/index`
|
|
205
|
+
- Trailing slash is removed (`/docs/` -> `/docs`)
|
|
206
|
+
- `.md` suffix is removed (`/docs.md` -> `/docs`)
|
|
207
|
+
|
|
208
|
+
## Compatibility / runtime
|
|
209
|
+
|
|
210
|
+
- Next.js: App Router only
|
|
211
|
+
- Proxy (`proxy.ts`): Edge runtime (Next.js proxy)
|
|
212
|
+
- Route handlers: can be Edge or Node depending on how you configure your Next route file; this library itself does not require Node-only APIs.
|
|
213
|
+
|
|
214
|
+
## Why not HTML -> Markdown conversion?
|
|
215
|
+
|
|
216
|
+
This package avoids HTML rewriting/conversion on purpose: it is hard to do reliably, and it risks leaking content. Instead, you provide an explicit Markdown representation via `getMarkdown()`.
|
|
217
|
+
|
|
218
|
+
## Auth parity patterns
|
|
219
|
+
|
|
220
|
+
This package intentionally makes `getMarkdown()` your policy boundary.
|
|
221
|
+
|
|
222
|
+
### 1) Reuse an existing access check
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
import { createMarkdownRoute } from 'next-ai-discovery';
|
|
226
|
+
import type { NextRequest } from 'next/server';
|
|
227
|
+
|
|
228
|
+
async function canViewPath(request: NextRequest, pathname: string) {
|
|
229
|
+
// Example only. Wire this to your auth/session logic.
|
|
230
|
+
// Return false for protected routes.
|
|
231
|
+
return !pathname.startsWith('/admin');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const handler = createMarkdownRoute({
|
|
235
|
+
async getMarkdown(pathname, request) {
|
|
236
|
+
if (!(await canViewPath(request, pathname))) {
|
|
237
|
+
// Recommended default: return null (404) to avoid leaking existence.
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return { body: `# ${pathname}\n` };
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
export const GET = handler;
|
|
246
|
+
export const HEAD = handler;
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### 2) 404 vs 401/403
|
|
250
|
+
|
|
251
|
+
If your HTML route would redirect unauthenticated users, decide whether your Markdown variant should:
|
|
252
|
+
|
|
253
|
+
- Return `404` (recommended for private areas)
|
|
254
|
+
- Return `401/403`
|
|
255
|
+
|
|
256
|
+
Because Next.js route handlers return `Response`, you can also wrap the handler and map outcomes.
|
|
257
|
+
|
|
258
|
+
## Roadmap
|
|
259
|
+
|
|
260
|
+
- frontmatter format options (yaml vs json vs none)
|
|
261
|
+
- llms-full helpers (sitemap-driven inventory)
|
|
262
|
+
- observability hooks (`onServed`, `onRewrite`) docs + examples
|
|
263
|
+
- codemod/snippets for adding `withMarkdownAlternate()`
|
|
264
|
+
|
|
265
|
+
## License
|
|
266
|
+
|
|
267
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_PROXY_EXCLUDE_PREFIXES = exports.DEFAULT_PROXY_EXCLUDE_EXACT = exports.DEFAULT_MARKDOWN_ACCEPT = exports.DEFAULT_ENDPOINT_PATH = exports.createMarkdownProxy = exports.pathnameToMd = exports.normalizePathname = exports.withMarkdownAlternate = exports.createMarkdownRoute = exports.createLlmsTxtRoute = exports.renderLlmsTxt = void 0;
|
|
4
|
+
/* biome-ignore lint/performance/noBarrelFile: public entrypoint exports */
|
|
5
|
+
var llms_js_1 = require("./llms.cjs");
|
|
6
|
+
Object.defineProperty(exports, "renderLlmsTxt", { enumerable: true, get: function () { return llms_js_1.renderLlmsTxt; } });
|
|
7
|
+
var llms_route_js_1 = require("./llms-route.cjs");
|
|
8
|
+
Object.defineProperty(exports, "createLlmsTxtRoute", { enumerable: true, get: function () { return llms_route_js_1.createLlmsTxtRoute; } });
|
|
9
|
+
var markdown_route_js_1 = require("./markdown-route.cjs");
|
|
10
|
+
Object.defineProperty(exports, "createMarkdownRoute", { enumerable: true, get: function () { return markdown_route_js_1.createMarkdownRoute; } });
|
|
11
|
+
var metadata_js_1 = require("./metadata.cjs");
|
|
12
|
+
Object.defineProperty(exports, "withMarkdownAlternate", { enumerable: true, get: function () { return metadata_js_1.withMarkdownAlternate; } });
|
|
13
|
+
var pathname_js_1 = require("./pathname.cjs");
|
|
14
|
+
Object.defineProperty(exports, "normalizePathname", { enumerable: true, get: function () { return pathname_js_1.normalizePathname; } });
|
|
15
|
+
Object.defineProperty(exports, "pathnameToMd", { enumerable: true, get: function () { return pathname_js_1.pathnameToMd; } });
|
|
16
|
+
var proxy_js_1 = require("./proxy.cjs");
|
|
17
|
+
Object.defineProperty(exports, "createMarkdownProxy", { enumerable: true, get: function () { return proxy_js_1.createMarkdownProxy; } });
|
|
18
|
+
Object.defineProperty(exports, "DEFAULT_ENDPOINT_PATH", { enumerable: true, get: function () { return proxy_js_1.DEFAULT_ENDPOINT_PATH; } });
|
|
19
|
+
Object.defineProperty(exports, "DEFAULT_MARKDOWN_ACCEPT", { enumerable: true, get: function () { return proxy_js_1.DEFAULT_MARKDOWN_ACCEPT; } });
|
|
20
|
+
Object.defineProperty(exports, "DEFAULT_PROXY_EXCLUDE_EXACT", { enumerable: true, get: function () { return proxy_js_1.DEFAULT_PROXY_EXCLUDE_EXACT; } });
|
|
21
|
+
Object.defineProperty(exports, "DEFAULT_PROXY_EXCLUDE_PREFIXES", { enumerable: true, get: function () { return proxy_js_1.DEFAULT_PROXY_EXCLUDE_PREFIXES; } });
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { renderLlmsTxt } from "./llms.cjs";
|
|
2
|
+
export { createLlmsTxtRoute } from "./llms-route.cjs";
|
|
3
|
+
export { createMarkdownRoute } from "./markdown-route.cjs";
|
|
4
|
+
export { withMarkdownAlternate } from "./metadata.cjs";
|
|
5
|
+
export { normalizePathname, pathnameToMd } from "./pathname.cjs";
|
|
6
|
+
export { createMarkdownProxy, DEFAULT_ENDPOINT_PATH, DEFAULT_MARKDOWN_ACCEPT, DEFAULT_PROXY_EXCLUDE_EXACT, DEFAULT_PROXY_EXCLUDE_PREFIXES, } from "./proxy.cjs";
|
|
7
|
+
export type { LlmsTxtConfig, LlmsTxtProvider, LlmsTxtVariant, } from "./llms.cjs";
|
|
8
|
+
export type { GetMarkdown, MarkdownContent, MarkdownRouteOptions, } from "./markdown-route.cjs";
|
|
9
|
+
export type { MarkdownAlternateOptions } from "./metadata.cjs";
|
|
10
|
+
export type { MarkdownProxyOptions, MarkdownRewriteType, } from "./proxy.cjs";
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,mBAAkB;AAC1C,OAAO,EAAE,kBAAkB,EAAE,yBAAwB;AACrD,OAAO,EAAE,mBAAmB,EAAE,6BAA4B;AAC1D,OAAO,EAAE,qBAAqB,EAAE,uBAAsB;AACtD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,uBAAsB;AAChE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,uBAAuB,EACvB,2BAA2B,EAC3B,8BAA8B,GAC/B,oBAAmB;AACpB,YAAY,EACV,aAAa,EACb,eAAe,EACf,cAAc,GACf,mBAAkB;AACnB,YAAY,EACV,WAAW,EACX,eAAe,EACf,oBAAoB,GACrB,6BAA4B;AAC7B,YAAY,EAAE,wBAAwB,EAAE,uBAAsB;AAC9D,YAAY,EACV,oBAAoB,EACpB,mBAAmB,GACpB,oBAAmB"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { renderLlmsTxt } from "./llms.js";
|
|
2
|
+
export { createLlmsTxtRoute } from "./llms-route.js";
|
|
3
|
+
export { createMarkdownRoute } from "./markdown-route.js";
|
|
4
|
+
export { withMarkdownAlternate } from "./metadata.js";
|
|
5
|
+
export { normalizePathname, pathnameToMd } from "./pathname.js";
|
|
6
|
+
export { createMarkdownProxy, DEFAULT_ENDPOINT_PATH, DEFAULT_MARKDOWN_ACCEPT, DEFAULT_PROXY_EXCLUDE_EXACT, DEFAULT_PROXY_EXCLUDE_PREFIXES, } from "./proxy.js";
|
|
7
|
+
export type { LlmsTxtConfig, LlmsTxtProvider, LlmsTxtVariant, } from "./llms.js";
|
|
8
|
+
export type { GetMarkdown, MarkdownContent, MarkdownRouteOptions, } from "./markdown-route.js";
|
|
9
|
+
export type { MarkdownAlternateOptions } from "./metadata.js";
|
|
10
|
+
export type { MarkdownProxyOptions, MarkdownRewriteType, } from "./proxy.js";
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,kBAAkB;AAC1C,OAAO,EAAE,kBAAkB,EAAE,wBAAwB;AACrD,OAAO,EAAE,mBAAmB,EAAE,4BAA4B;AAC1D,OAAO,EAAE,qBAAqB,EAAE,sBAAsB;AACtD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,sBAAsB;AAChE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,uBAAuB,EACvB,2BAA2B,EAC3B,8BAA8B,GAC/B,mBAAmB;AACpB,YAAY,EACV,aAAa,EACb,eAAe,EACf,cAAc,GACf,kBAAkB;AACnB,YAAY,EACV,WAAW,EACX,eAAe,EACf,oBAAoB,GACrB,4BAA4B;AAC7B,YAAY,EAAE,wBAAwB,EAAE,sBAAsB;AAC9D,YAAY,EACV,oBAAoB,EACpB,mBAAmB,GACpB,mBAAmB"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/* biome-ignore lint/performance/noBarrelFile: public entrypoint exports */
|
|
2
|
+
export { renderLlmsTxt } from "./llms.js";
|
|
3
|
+
export { createLlmsTxtRoute } from "./llms-route.js";
|
|
4
|
+
export { createMarkdownRoute } from "./markdown-route.js";
|
|
5
|
+
export { withMarkdownAlternate } from "./metadata.js";
|
|
6
|
+
export { normalizePathname, pathnameToMd } from "./pathname.js";
|
|
7
|
+
export { createMarkdownProxy, DEFAULT_ENDPOINT_PATH, DEFAULT_MARKDOWN_ACCEPT, DEFAULT_PROXY_EXCLUDE_EXACT, DEFAULT_PROXY_EXCLUDE_PREFIXES, } from "./proxy.js";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createLlmsTxtRoute = void 0;
|
|
4
|
+
const llms_js_1 = require("./llms.cjs");
|
|
5
|
+
const DEFAULT_HEADERS = {
|
|
6
|
+
'Content-Type': 'text/markdown; charset=utf-8',
|
|
7
|
+
};
|
|
8
|
+
const resolveConfig = (options, variant) => {
|
|
9
|
+
if ('config' in options && options.config) {
|
|
10
|
+
return options.config;
|
|
11
|
+
}
|
|
12
|
+
return options.getLlmsTxt(variant);
|
|
13
|
+
};
|
|
14
|
+
const handleError = (error) => {
|
|
15
|
+
console.error('[next-ai-discovery] llms.txt route error', error);
|
|
16
|
+
return new Response('Internal Server Error', {
|
|
17
|
+
status: 500,
|
|
18
|
+
headers: DEFAULT_HEADERS,
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
const createLlmsTxtRoute = (options) => {
|
|
22
|
+
const variant = options.variant ?? 'default';
|
|
23
|
+
return async (_request) => {
|
|
24
|
+
try {
|
|
25
|
+
const config = await resolveConfig(options, variant);
|
|
26
|
+
const body = (0, llms_js_1.renderLlmsTxt)(config, variant);
|
|
27
|
+
return new Response(body, {
|
|
28
|
+
status: 200,
|
|
29
|
+
headers: DEFAULT_HEADERS,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
return handleError(error);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
exports.createLlmsTxtRoute = createLlmsTxtRoute;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { NextRequest } from 'next/server.js';
|
|
2
|
+
import type { LlmsTxtConfig, LlmsTxtProvider, LlmsTxtVariant } from "./llms.cjs";
|
|
3
|
+
export type LlmsTxtRouteOptions = {
|
|
4
|
+
config: LlmsTxtConfig;
|
|
5
|
+
getLlmsTxt?: never;
|
|
6
|
+
variant?: LlmsTxtVariant;
|
|
7
|
+
} | {
|
|
8
|
+
config?: never;
|
|
9
|
+
getLlmsTxt: LlmsTxtProvider;
|
|
10
|
+
variant?: LlmsTxtVariant;
|
|
11
|
+
};
|
|
12
|
+
export declare const createLlmsTxtRoute: (options: LlmsTxtRouteOptions) => (_request: NextRequest) => Promise<Response>;
|
|
13
|
+
//# sourceMappingURL=llms-route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llms-route.d.ts","sourceRoot":"","sources":["../src/llms-route.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,mBAAkB;AAEhF,MAAM,MAAM,mBAAmB,GAC3B;IACE,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,CAAC,EAAE,KAAK,CAAC;IACnB,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B,GACD;IACE,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,UAAU,EAAE,eAAe,CAAC;IAC5B,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B,CAAC;AAyBN,eAAO,MAAM,kBAAkB,GAAI,SAAS,mBAAmB,MAG/C,UAAU,WAAW,sBAapC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { NextRequest } from 'next/server.js';
|
|
2
|
+
import type { LlmsTxtConfig, LlmsTxtProvider, LlmsTxtVariant } from "./llms.js";
|
|
3
|
+
export type LlmsTxtRouteOptions = {
|
|
4
|
+
config: LlmsTxtConfig;
|
|
5
|
+
getLlmsTxt?: never;
|
|
6
|
+
variant?: LlmsTxtVariant;
|
|
7
|
+
} | {
|
|
8
|
+
config?: never;
|
|
9
|
+
getLlmsTxt: LlmsTxtProvider;
|
|
10
|
+
variant?: LlmsTxtVariant;
|
|
11
|
+
};
|
|
12
|
+
export declare const createLlmsTxtRoute: (options: LlmsTxtRouteOptions) => (_request: NextRequest) => Promise<Response>;
|
|
13
|
+
//# sourceMappingURL=llms-route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llms-route.d.ts","sourceRoot":"","sources":["../src/llms-route.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,kBAAkB;AAEhF,MAAM,MAAM,mBAAmB,GAC3B;IACE,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,CAAC,EAAE,KAAK,CAAC;IACnB,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B,GACD;IACE,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,UAAU,EAAE,eAAe,CAAC;IAC5B,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B,CAAC;AAyBN,eAAO,MAAM,kBAAkB,GAAI,SAAS,mBAAmB,MAG/C,UAAU,WAAW,sBAapC,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { renderLlmsTxt } from "./llms.js";
|
|
2
|
+
const DEFAULT_HEADERS = {
|
|
3
|
+
'Content-Type': 'text/markdown; charset=utf-8',
|
|
4
|
+
};
|
|
5
|
+
const resolveConfig = (options, variant) => {
|
|
6
|
+
if ('config' in options && options.config) {
|
|
7
|
+
return options.config;
|
|
8
|
+
}
|
|
9
|
+
return options.getLlmsTxt(variant);
|
|
10
|
+
};
|
|
11
|
+
const handleError = (error) => {
|
|
12
|
+
console.error('[next-ai-discovery] llms.txt route error', error);
|
|
13
|
+
return new Response('Internal Server Error', {
|
|
14
|
+
status: 500,
|
|
15
|
+
headers: DEFAULT_HEADERS,
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
export const createLlmsTxtRoute = (options) => {
|
|
19
|
+
const variant = options.variant ?? 'default';
|
|
20
|
+
return async (_request) => {
|
|
21
|
+
try {
|
|
22
|
+
const config = await resolveConfig(options, variant);
|
|
23
|
+
const body = renderLlmsTxt(config, variant);
|
|
24
|
+
return new Response(body, {
|
|
25
|
+
status: 200,
|
|
26
|
+
headers: DEFAULT_HEADERS,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
return handleError(error);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
};
|
package/dist/llms.cjs
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderLlmsTxt = void 0;
|
|
4
|
+
const formatSection = (section) => {
|
|
5
|
+
const lines = [`## ${section.title}`];
|
|
6
|
+
for (const item of section.items) {
|
|
7
|
+
lines.push(`- ${item}`);
|
|
8
|
+
}
|
|
9
|
+
return lines.join('\n');
|
|
10
|
+
};
|
|
11
|
+
const joinBlocks = (blocks) => blocks.filter(block => block.trim().length > 0).join('\n\n');
|
|
12
|
+
const renderLlmsTxt = (config, variant) => {
|
|
13
|
+
const blocks = [`# ${config.site.name}`];
|
|
14
|
+
if (config.site.description) {
|
|
15
|
+
blocks.push(config.site.description);
|
|
16
|
+
}
|
|
17
|
+
if (config.sections && config.sections.length > 0) {
|
|
18
|
+
const rendered = config.sections.map(section => formatSection(section));
|
|
19
|
+
blocks.push(joinBlocks(rendered));
|
|
20
|
+
}
|
|
21
|
+
if (config.markdown) {
|
|
22
|
+
const markdownLines = ['## Machine-readable variants'];
|
|
23
|
+
if (config.markdown.appendDotMd ?? true) {
|
|
24
|
+
markdownLines.push('- Markdown pages: append `.md` to most URLs');
|
|
25
|
+
}
|
|
26
|
+
if (config.markdown.acceptNegotiation ?? true) {
|
|
27
|
+
markdownLines.push('- Negotiation: send `Accept: text/markdown`');
|
|
28
|
+
}
|
|
29
|
+
if (variant === 'default') {
|
|
30
|
+
const fullIndex = config.markdown.fullIndexPath ?? '/llms-full.txt';
|
|
31
|
+
markdownLines.push(`- Full index: ${fullIndex}`);
|
|
32
|
+
}
|
|
33
|
+
blocks.push(markdownLines.join('\n'));
|
|
34
|
+
}
|
|
35
|
+
if (config.site.url) {
|
|
36
|
+
blocks.push(`Site: ${config.site.url}`);
|
|
37
|
+
}
|
|
38
|
+
return joinBlocks(blocks);
|
|
39
|
+
};
|
|
40
|
+
exports.renderLlmsTxt = renderLlmsTxt;
|
package/dist/llms.d.cts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type LlmsTxtVariant = 'default' | 'full';
|
|
2
|
+
export type LlmsTxtConfig = {
|
|
3
|
+
site: {
|
|
4
|
+
name: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
url?: string;
|
|
7
|
+
};
|
|
8
|
+
sections?: Array<{
|
|
9
|
+
title: string;
|
|
10
|
+
items: Array<string>;
|
|
11
|
+
}>;
|
|
12
|
+
markdown?: {
|
|
13
|
+
appendDotMd?: boolean;
|
|
14
|
+
acceptNegotiation?: boolean;
|
|
15
|
+
fullIndexPath?: string;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export type LlmsTxtProvider = (variant: LlmsTxtVariant) => Promise<LlmsTxtConfig> | LlmsTxtConfig;
|
|
19
|
+
export declare const renderLlmsTxt: (config: LlmsTxtConfig, variant: LlmsTxtVariant) => string;
|
|
20
|
+
//# sourceMappingURL=llms.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llms.d.ts","sourceRoot":"","sources":["../src/llms.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,MAAM,CAAC;AAEhD,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;IACF,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;KAAE,CAAC,CAAC;IAC1D,QAAQ,CAAC,EAAE;QACT,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,CAC5B,OAAO,EAAE,cAAc,KACpB,OAAO,CAAC,aAAa,CAAC,GAAG,aAAa,CAAC;AAa5C,eAAO,MAAM,aAAa,GACxB,QAAQ,aAAa,EACrB,SAAS,cAAc,WAkCxB,CAAC"}
|
package/dist/llms.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type LlmsTxtVariant = 'default' | 'full';
|
|
2
|
+
export type LlmsTxtConfig = {
|
|
3
|
+
site: {
|
|
4
|
+
name: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
url?: string;
|
|
7
|
+
};
|
|
8
|
+
sections?: Array<{
|
|
9
|
+
title: string;
|
|
10
|
+
items: Array<string>;
|
|
11
|
+
}>;
|
|
12
|
+
markdown?: {
|
|
13
|
+
appendDotMd?: boolean;
|
|
14
|
+
acceptNegotiation?: boolean;
|
|
15
|
+
fullIndexPath?: string;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export type LlmsTxtProvider = (variant: LlmsTxtVariant) => Promise<LlmsTxtConfig> | LlmsTxtConfig;
|
|
19
|
+
export declare const renderLlmsTxt: (config: LlmsTxtConfig, variant: LlmsTxtVariant) => string;
|
|
20
|
+
//# sourceMappingURL=llms.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llms.d.ts","sourceRoot":"","sources":["../src/llms.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,MAAM,CAAC;AAEhD,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;IACF,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;KAAE,CAAC,CAAC;IAC1D,QAAQ,CAAC,EAAE;QACT,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,CAC5B,OAAO,EAAE,cAAc,KACpB,OAAO,CAAC,aAAa,CAAC,GAAG,aAAa,CAAC;AAa5C,eAAO,MAAM,aAAa,GACxB,QAAQ,aAAa,EACrB,SAAS,cAAc,WAkCxB,CAAC"}
|
package/dist/llms.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const formatSection = (section) => {
|
|
2
|
+
const lines = [`## ${section.title}`];
|
|
3
|
+
for (const item of section.items) {
|
|
4
|
+
lines.push(`- ${item}`);
|
|
5
|
+
}
|
|
6
|
+
return lines.join('\n');
|
|
7
|
+
};
|
|
8
|
+
const joinBlocks = (blocks) => blocks.filter(block => block.trim().length > 0).join('\n\n');
|
|
9
|
+
export const renderLlmsTxt = (config, variant) => {
|
|
10
|
+
const blocks = [`# ${config.site.name}`];
|
|
11
|
+
if (config.site.description) {
|
|
12
|
+
blocks.push(config.site.description);
|
|
13
|
+
}
|
|
14
|
+
if (config.sections && config.sections.length > 0) {
|
|
15
|
+
const rendered = config.sections.map(section => formatSection(section));
|
|
16
|
+
blocks.push(joinBlocks(rendered));
|
|
17
|
+
}
|
|
18
|
+
if (config.markdown) {
|
|
19
|
+
const markdownLines = ['## Machine-readable variants'];
|
|
20
|
+
if (config.markdown.appendDotMd ?? true) {
|
|
21
|
+
markdownLines.push('- Markdown pages: append `.md` to most URLs');
|
|
22
|
+
}
|
|
23
|
+
if (config.markdown.acceptNegotiation ?? true) {
|
|
24
|
+
markdownLines.push('- Negotiation: send `Accept: text/markdown`');
|
|
25
|
+
}
|
|
26
|
+
if (variant === 'default') {
|
|
27
|
+
const fullIndex = config.markdown.fullIndexPath ?? '/llms-full.txt';
|
|
28
|
+
markdownLines.push(`- Full index: ${fullIndex}`);
|
|
29
|
+
}
|
|
30
|
+
blocks.push(markdownLines.join('\n'));
|
|
31
|
+
}
|
|
32
|
+
if (config.site.url) {
|
|
33
|
+
blocks.push(`Site: ${config.site.url}`);
|
|
34
|
+
}
|
|
35
|
+
return joinBlocks(blocks);
|
|
36
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createMarkdownRoute = void 0;
|
|
4
|
+
const pathname_js_1 = require("./pathname.cjs");
|
|
5
|
+
const DEFAULT_HEADERS = {
|
|
6
|
+
'Content-Type': 'text/markdown; charset=utf-8',
|
|
7
|
+
Vary: 'Accept',
|
|
8
|
+
};
|
|
9
|
+
const toFrontmatter = (frontmatter) => {
|
|
10
|
+
const lines = ['---'];
|
|
11
|
+
for (const [key, value] of Object.entries(frontmatter)) {
|
|
12
|
+
lines.push(`${key}: ${JSON.stringify(value)}`);
|
|
13
|
+
}
|
|
14
|
+
lines.push('---');
|
|
15
|
+
return lines.join('\n');
|
|
16
|
+
};
|
|
17
|
+
const buildBody = (content, includeFrontmatter) => {
|
|
18
|
+
if (!(includeFrontmatter && content.frontmatter)) {
|
|
19
|
+
return content.body;
|
|
20
|
+
}
|
|
21
|
+
return `${toFrontmatter(content.frontmatter)}\n\n${content.body}`;
|
|
22
|
+
};
|
|
23
|
+
const handleError = (error) => {
|
|
24
|
+
console.error('[next-ai-discovery] markdown route error', error);
|
|
25
|
+
return new Response('Internal Server Error', {
|
|
26
|
+
status: 500,
|
|
27
|
+
headers: DEFAULT_HEADERS,
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
const createMarkdownRoute = (options) => {
|
|
31
|
+
const includeFrontmatter = options.includeFrontmatter ?? true;
|
|
32
|
+
return async (request) => {
|
|
33
|
+
const url = new URL(request.url);
|
|
34
|
+
const rawPath = url.searchParams.get('path') ?? '/';
|
|
35
|
+
const pathname = (0, pathname_js_1.normalizePathname)(rawPath);
|
|
36
|
+
try {
|
|
37
|
+
const content = await options.getMarkdown(pathname, request);
|
|
38
|
+
const isHead = request.method === 'HEAD';
|
|
39
|
+
if (!content) {
|
|
40
|
+
options.onServed?.({ pathname, status: 404 });
|
|
41
|
+
return new Response(isHead ? null : 'Not Found', {
|
|
42
|
+
status: 404,
|
|
43
|
+
headers: DEFAULT_HEADERS,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const body = buildBody(content, includeFrontmatter);
|
|
47
|
+
options.onServed?.({ pathname, status: 200 });
|
|
48
|
+
return new Response(isHead ? null : body, {
|
|
49
|
+
status: 200,
|
|
50
|
+
headers: DEFAULT_HEADERS,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
options.onServed?.({ pathname, status: 500 });
|
|
55
|
+
return handleError(error);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
exports.createMarkdownRoute = createMarkdownRoute;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { NextRequest } from 'next/server.js';
|
|
2
|
+
export type MarkdownContent = {
|
|
3
|
+
body: string;
|
|
4
|
+
frontmatter?: Record<string, unknown>;
|
|
5
|
+
};
|
|
6
|
+
export type GetMarkdown = (pathname: string, request: NextRequest) => Promise<MarkdownContent | null> | MarkdownContent | null;
|
|
7
|
+
export type MarkdownRouteOptions = {
|
|
8
|
+
getMarkdown: GetMarkdown;
|
|
9
|
+
includeFrontmatter?: boolean;
|
|
10
|
+
onServed?: (event: {
|
|
11
|
+
pathname: string;
|
|
12
|
+
status: number;
|
|
13
|
+
}) => void;
|
|
14
|
+
};
|
|
15
|
+
export declare const createMarkdownRoute: (options: MarkdownRouteOptions) => (request: NextRequest) => Promise<Response>;
|
|
16
|
+
//# sourceMappingURL=markdown-route.d.ts.map
|