@publish.os/nextjs 0.0.1-alpha.3 → 0.0.1-alpha.4
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 +3 -9
- package/dist/index.cjs +26 -23
- package/dist/index.js +26 -23
- package/dist/kernel/routes/agent/markdown.cjs +5 -11
- package/dist/kernel/routes/agent/markdown.d.cts +0 -1
- package/dist/kernel/routes/agent/markdown.d.ts +0 -1
- package/dist/kernel/routes/agent/markdown.js +5 -11
- package/dist/kernel/routes/metadata/feed.cjs +17 -21
- package/dist/kernel/routes/metadata/feed.d.cts +0 -1
- package/dist/kernel/routes/metadata/feed.d.ts +0 -1
- package/dist/kernel/routes/metadata/feed.js +17 -21
- package/dist/kernel/routes/metadata/llms.cjs +21 -16
- package/dist/kernel/routes/metadata/llms.d.cts +0 -1
- package/dist/kernel/routes/metadata/llms.d.ts +0 -1
- package/dist/kernel/routes/metadata/llms.js +21 -16
- package/dist/kernel/routes/metadata/robots.cjs +21 -11
- package/dist/kernel/routes/metadata/robots.js +21 -11
- package/dist/kernel/routes/metadata/sitemap.cjs +29 -19
- package/dist/kernel/routes/metadata/sitemap.js +29 -19
- package/package.json +26 -2
- package/release/package.json +23 -0
- package/release/pnpm-lock.yaml +1192 -0
- package/release/release.json +39 -0
package/README.md
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
# @publish.os/nextjs
|
|
2
2
|
|
|
3
|
-
PublishOS runtime package for generated Next.js applications.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Install
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
pnpm add @publish.os/nextjs
|
|
11
|
-
```
|
|
3
|
+
PublishOS runtime package for generated Next.js applications. Tenant source
|
|
4
|
+
pins its exact version in `publish.os.config.json`; the CLI obtains and verifies
|
|
5
|
+
it automatically. Do not install it into the host application.
|
|
12
6
|
|
|
13
7
|
## License
|
|
14
8
|
|
package/dist/index.cjs
CHANGED
|
@@ -178,22 +178,28 @@ function cachedTextHeaders(contentType, options = {}) {
|
|
|
178
178
|
headers.set("vercel-cdn-cache-control", cdnCacheControl(options.cachePolicy));
|
|
179
179
|
headers.set("content-type", contentType);
|
|
180
180
|
headers.set("x-content-type-options", "nosniff");
|
|
181
|
-
if (options.etag) headers.set("etag", options.etag);
|
|
182
181
|
if (options.noindex) headers.set("x-robots-tag", "noindex");
|
|
183
182
|
return headers;
|
|
184
183
|
}
|
|
185
184
|
function createCachedTextResponse(options) {
|
|
186
185
|
const status = options.status ?? 200;
|
|
187
|
-
const method = options.method ??
|
|
186
|
+
const method = options.method ?? "GET";
|
|
188
187
|
const headers = cachedTextHeaders(options.contentType, options);
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
188
|
+
return new Response(method === "HEAD" ? null : options.body, {
|
|
189
|
+
headers,
|
|
190
|
+
status
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// src/kernel/routes/metadata/canonical-url.ts
|
|
195
|
+
function canonicalUrl(manifest, path) {
|
|
196
|
+
const origin = manifest.site.url?.replace(/\/+$/, "");
|
|
197
|
+
if (!origin) return path;
|
|
198
|
+
try {
|
|
199
|
+
return new URL(path, `${origin}/`).toString();
|
|
200
|
+
} catch {
|
|
201
|
+
return path;
|
|
202
|
+
}
|
|
197
203
|
}
|
|
198
204
|
|
|
199
205
|
// src/kernel/routes/metadata/feed.ts
|
|
@@ -206,15 +212,6 @@ function pageTimestamp(page) {
|
|
|
206
212
|
const timestamp = Date.parse(value);
|
|
207
213
|
return Number.isNaN(timestamp) ? -Infinity : timestamp;
|
|
208
214
|
}
|
|
209
|
-
function canonicalUrl(manifest, path) {
|
|
210
|
-
const origin = manifest.site.url?.replace(/\/+$/, "");
|
|
211
|
-
if (!origin) return path;
|
|
212
|
-
try {
|
|
213
|
-
return new URL(path, `${origin}/`).toString();
|
|
214
|
-
} catch {
|
|
215
|
-
return path;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
215
|
function pageInScope(page, options) {
|
|
219
216
|
if (options.scope === "site" || !options.group) return true;
|
|
220
217
|
if (options.scope === "self") return page.group === options.group;
|
|
@@ -379,10 +376,10 @@ async function llmsTxt(manifest, options = {}) {
|
|
|
379
376
|
const visiblePages = options.limit ? pages.slice(0, options.limit) : pages;
|
|
380
377
|
const lines = visiblePages.map((page) => {
|
|
381
378
|
const description = options.includeDescriptions && page.description ? ` - ${page.description}` : "";
|
|
382
|
-
return `- [${page.title}](${page.path})${description}`;
|
|
379
|
+
return `- [${page.title}](${canonicalUrl(manifest, page.path)})${description}`;
|
|
383
380
|
});
|
|
384
381
|
const linkLines = (options.links ?? []).map(
|
|
385
|
-
(link) => `- [${link.title}](${link.path})`
|
|
382
|
+
(link) => `- [${link.title}](${canonicalUrl(manifest, link.path)})`
|
|
386
383
|
);
|
|
387
384
|
const linkSection = linkLines.length ? `
|
|
388
385
|
|
|
@@ -1012,7 +1009,11 @@ async function robotsTxt(manifest) {
|
|
|
1012
1009
|
"use cache";
|
|
1013
1010
|
(0, import_cache3.cacheLife)("page");
|
|
1014
1011
|
(0, import_cache3.cacheTag)(tags.site(), tags.robots(), tags.manifest(manifest.contentHash));
|
|
1015
|
-
|
|
1012
|
+
const sitemapUrl = canonicalUrl(manifest, "/sitemap.xml");
|
|
1013
|
+
return `User-agent: *
|
|
1014
|
+
Allow: /
|
|
1015
|
+
Sitemap: ${sitemapUrl}
|
|
1016
|
+
`;
|
|
1016
1017
|
}
|
|
1017
1018
|
function createRobotsTxtRoute(manifest) {
|
|
1018
1019
|
return async function GET() {
|
|
@@ -1038,7 +1039,9 @@ async function sitemapXml(manifest) {
|
|
|
1038
1039
|
...manifest.pages.filter((page) => page.status === "published").map((page) => page.path)
|
|
1039
1040
|
];
|
|
1040
1041
|
const uniqueUrls = [...new Set(urls)];
|
|
1041
|
-
const body = uniqueUrls.map(
|
|
1042
|
+
const body = uniqueUrls.map(
|
|
1043
|
+
(url) => ` <url><loc>${xmlEscape2(canonicalUrl(manifest, url))}</loc></url>`
|
|
1044
|
+
).join("\n");
|
|
1042
1045
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
1043
1046
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
1044
1047
|
${body}
|
package/dist/index.js
CHANGED
|
@@ -132,22 +132,28 @@ function cachedTextHeaders(contentType, options = {}) {
|
|
|
132
132
|
headers.set("vercel-cdn-cache-control", cdnCacheControl(options.cachePolicy));
|
|
133
133
|
headers.set("content-type", contentType);
|
|
134
134
|
headers.set("x-content-type-options", "nosniff");
|
|
135
|
-
if (options.etag) headers.set("etag", options.etag);
|
|
136
135
|
if (options.noindex) headers.set("x-robots-tag", "noindex");
|
|
137
136
|
return headers;
|
|
138
137
|
}
|
|
139
138
|
function createCachedTextResponse(options) {
|
|
140
139
|
const status = options.status ?? 200;
|
|
141
|
-
const method = options.method ??
|
|
140
|
+
const method = options.method ?? "GET";
|
|
142
141
|
const headers = cachedTextHeaders(options.contentType, options);
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
142
|
+
return new Response(method === "HEAD" ? null : options.body, {
|
|
143
|
+
headers,
|
|
144
|
+
status
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/kernel/routes/metadata/canonical-url.ts
|
|
149
|
+
function canonicalUrl(manifest, path) {
|
|
150
|
+
const origin = manifest.site.url?.replace(/\/+$/, "");
|
|
151
|
+
if (!origin) return path;
|
|
152
|
+
try {
|
|
153
|
+
return new URL(path, `${origin}/`).toString();
|
|
154
|
+
} catch {
|
|
155
|
+
return path;
|
|
156
|
+
}
|
|
151
157
|
}
|
|
152
158
|
|
|
153
159
|
// src/kernel/routes/metadata/feed.ts
|
|
@@ -160,15 +166,6 @@ function pageTimestamp(page) {
|
|
|
160
166
|
const timestamp = Date.parse(value);
|
|
161
167
|
return Number.isNaN(timestamp) ? -Infinity : timestamp;
|
|
162
168
|
}
|
|
163
|
-
function canonicalUrl(manifest, path) {
|
|
164
|
-
const origin = manifest.site.url?.replace(/\/+$/, "");
|
|
165
|
-
if (!origin) return path;
|
|
166
|
-
try {
|
|
167
|
-
return new URL(path, `${origin}/`).toString();
|
|
168
|
-
} catch {
|
|
169
|
-
return path;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
169
|
function pageInScope(page, options) {
|
|
173
170
|
if (options.scope === "site" || !options.group) return true;
|
|
174
171
|
if (options.scope === "self") return page.group === options.group;
|
|
@@ -333,10 +330,10 @@ async function llmsTxt(manifest, options = {}) {
|
|
|
333
330
|
const visiblePages = options.limit ? pages.slice(0, options.limit) : pages;
|
|
334
331
|
const lines = visiblePages.map((page) => {
|
|
335
332
|
const description = options.includeDescriptions && page.description ? ` - ${page.description}` : "";
|
|
336
|
-
return `- [${page.title}](${page.path})${description}`;
|
|
333
|
+
return `- [${page.title}](${canonicalUrl(manifest, page.path)})${description}`;
|
|
337
334
|
});
|
|
338
335
|
const linkLines = (options.links ?? []).map(
|
|
339
|
-
(link) => `- [${link.title}](${link.path})`
|
|
336
|
+
(link) => `- [${link.title}](${canonicalUrl(manifest, link.path)})`
|
|
340
337
|
);
|
|
341
338
|
const linkSection = linkLines.length ? `
|
|
342
339
|
|
|
@@ -966,7 +963,11 @@ async function robotsTxt(manifest) {
|
|
|
966
963
|
"use cache";
|
|
967
964
|
cacheLife3("page");
|
|
968
965
|
cacheTag3(tags.site(), tags.robots(), tags.manifest(manifest.contentHash));
|
|
969
|
-
|
|
966
|
+
const sitemapUrl = canonicalUrl(manifest, "/sitemap.xml");
|
|
967
|
+
return `User-agent: *
|
|
968
|
+
Allow: /
|
|
969
|
+
Sitemap: ${sitemapUrl}
|
|
970
|
+
`;
|
|
970
971
|
}
|
|
971
972
|
function createRobotsTxtRoute(manifest) {
|
|
972
973
|
return async function GET() {
|
|
@@ -992,7 +993,9 @@ async function sitemapXml(manifest) {
|
|
|
992
993
|
...manifest.pages.filter((page) => page.status === "published").map((page) => page.path)
|
|
993
994
|
];
|
|
994
995
|
const uniqueUrls = [...new Set(urls)];
|
|
995
|
-
const body = uniqueUrls.map(
|
|
996
|
+
const body = uniqueUrls.map(
|
|
997
|
+
(url) => ` <url><loc>${xmlEscape2(canonicalUrl(manifest, url))}</loc></url>`
|
|
998
|
+
).join("\n");
|
|
996
999
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
997
1000
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
998
1001
|
${body}
|
|
@@ -65,22 +65,17 @@ function cachedTextHeaders(contentType, options = {}) {
|
|
|
65
65
|
headers.set("vercel-cdn-cache-control", cdnCacheControl(options.cachePolicy));
|
|
66
66
|
headers.set("content-type", contentType);
|
|
67
67
|
headers.set("x-content-type-options", "nosniff");
|
|
68
|
-
if (options.etag) headers.set("etag", options.etag);
|
|
69
68
|
if (options.noindex) headers.set("x-robots-tag", "noindex");
|
|
70
69
|
return headers;
|
|
71
70
|
}
|
|
72
71
|
function createCachedTextResponse(options) {
|
|
73
72
|
const status = options.status ?? 200;
|
|
74
|
-
const method = options.method ??
|
|
73
|
+
const method = options.method ?? "GET";
|
|
75
74
|
const headers = cachedTextHeaders(options.contentType, options);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
headers,
|
|
81
|
-
status: isNotModified ? 304 : status
|
|
82
|
-
}
|
|
83
|
-
);
|
|
75
|
+
return new Response(method === "HEAD" ? null : options.body, {
|
|
76
|
+
headers,
|
|
77
|
+
status
|
|
78
|
+
});
|
|
84
79
|
}
|
|
85
80
|
|
|
86
81
|
// src/kernel/routes/agent/markdown.ts
|
|
@@ -181,7 +176,6 @@ async function markdownResponse(artifacts, discoveryLinks, method, context) {
|
|
|
181
176
|
return createCachedTextResponse({
|
|
182
177
|
body: artifact.body,
|
|
183
178
|
contentType,
|
|
184
|
-
etag: artifact.etag,
|
|
185
179
|
headers,
|
|
186
180
|
method,
|
|
187
181
|
status: 200
|
|
@@ -38,22 +38,17 @@ function cachedTextHeaders(contentType, options = {}) {
|
|
|
38
38
|
headers.set("vercel-cdn-cache-control", cdnCacheControl(options.cachePolicy));
|
|
39
39
|
headers.set("content-type", contentType);
|
|
40
40
|
headers.set("x-content-type-options", "nosniff");
|
|
41
|
-
if (options.etag) headers.set("etag", options.etag);
|
|
42
41
|
if (options.noindex) headers.set("x-robots-tag", "noindex");
|
|
43
42
|
return headers;
|
|
44
43
|
}
|
|
45
44
|
function createCachedTextResponse(options) {
|
|
46
45
|
const status = options.status ?? 200;
|
|
47
|
-
const method = options.method ??
|
|
46
|
+
const method = options.method ?? "GET";
|
|
48
47
|
const headers = cachedTextHeaders(options.contentType, options);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
headers,
|
|
54
|
-
status: isNotModified ? 304 : status
|
|
55
|
-
}
|
|
56
|
-
);
|
|
48
|
+
return new Response(method === "HEAD" ? null : options.body, {
|
|
49
|
+
headers,
|
|
50
|
+
status
|
|
51
|
+
});
|
|
57
52
|
}
|
|
58
53
|
|
|
59
54
|
// src/kernel/routes/agent/markdown.ts
|
|
@@ -154,7 +149,6 @@ async function markdownResponse(artifacts, discoveryLinks, method, context) {
|
|
|
154
149
|
return createCachedTextResponse({
|
|
155
150
|
body: artifact.body,
|
|
156
151
|
contentType,
|
|
157
|
-
etag: artifact.etag,
|
|
158
152
|
headers,
|
|
159
153
|
method,
|
|
160
154
|
status: 200
|
|
@@ -102,22 +102,28 @@ function cachedTextHeaders(contentType, options = {}) {
|
|
|
102
102
|
headers.set("vercel-cdn-cache-control", cdnCacheControl(options.cachePolicy));
|
|
103
103
|
headers.set("content-type", contentType);
|
|
104
104
|
headers.set("x-content-type-options", "nosniff");
|
|
105
|
-
if (options.etag) headers.set("etag", options.etag);
|
|
106
105
|
if (options.noindex) headers.set("x-robots-tag", "noindex");
|
|
107
106
|
return headers;
|
|
108
107
|
}
|
|
109
108
|
function createCachedTextResponse(options) {
|
|
110
109
|
const status = options.status ?? 200;
|
|
111
|
-
const method = options.method ??
|
|
110
|
+
const method = options.method ?? "GET";
|
|
112
111
|
const headers = cachedTextHeaders(options.contentType, options);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
112
|
+
return new Response(method === "HEAD" ? null : options.body, {
|
|
113
|
+
headers,
|
|
114
|
+
status
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/kernel/routes/metadata/canonical-url.ts
|
|
119
|
+
function canonicalUrl(manifest, path) {
|
|
120
|
+
const origin = manifest.site.url?.replace(/\/+$/, "");
|
|
121
|
+
if (!origin) return path;
|
|
122
|
+
try {
|
|
123
|
+
return new URL(path, `${origin}/`).toString();
|
|
124
|
+
} catch {
|
|
125
|
+
return path;
|
|
126
|
+
}
|
|
121
127
|
}
|
|
122
128
|
|
|
123
129
|
// src/kernel/routes/metadata/feed.ts
|
|
@@ -130,15 +136,6 @@ function pageTimestamp(page) {
|
|
|
130
136
|
const timestamp = Date.parse(value);
|
|
131
137
|
return Number.isNaN(timestamp) ? -Infinity : timestamp;
|
|
132
138
|
}
|
|
133
|
-
function canonicalUrl(manifest, path) {
|
|
134
|
-
const origin = manifest.site.url?.replace(/\/+$/, "");
|
|
135
|
-
if (!origin) return path;
|
|
136
|
-
try {
|
|
137
|
-
return new URL(path, `${origin}/`).toString();
|
|
138
|
-
} catch {
|
|
139
|
-
return path;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
139
|
function pageInScope(page, options) {
|
|
143
140
|
if (options.scope === "site" || !options.group) return true;
|
|
144
141
|
if (options.scope === "self") return page.group === options.group;
|
|
@@ -283,8 +280,7 @@ function createPrecomputedFeedRoute(output) {
|
|
|
283
280
|
return function GET() {
|
|
284
281
|
return createCachedTextResponse({
|
|
285
282
|
body: output.body,
|
|
286
|
-
contentType: output.contentType
|
|
287
|
-
etag: output.etag
|
|
283
|
+
contentType: output.contentType
|
|
288
284
|
});
|
|
289
285
|
};
|
|
290
286
|
}
|
|
@@ -11,7 +11,6 @@ type FeedRouteOptions = {
|
|
|
11
11
|
type PrecomputedFeedOutput = {
|
|
12
12
|
body: string;
|
|
13
13
|
contentType: string;
|
|
14
|
-
etag?: string;
|
|
15
14
|
};
|
|
16
15
|
declare function createRssFeedRoute(manifest: ManifestLike, options?: FeedRouteOptions): () => Promise<Response>;
|
|
17
16
|
declare function createAtomFeedRoute(manifest: ManifestLike, options?: FeedRouteOptions): () => Promise<Response>;
|
|
@@ -11,7 +11,6 @@ type FeedRouteOptions = {
|
|
|
11
11
|
type PrecomputedFeedOutput = {
|
|
12
12
|
body: string;
|
|
13
13
|
contentType: string;
|
|
14
|
-
etag?: string;
|
|
15
14
|
};
|
|
16
15
|
declare function createRssFeedRoute(manifest: ManifestLike, options?: FeedRouteOptions): () => Promise<Response>;
|
|
17
16
|
declare function createAtomFeedRoute(manifest: ManifestLike, options?: FeedRouteOptions): () => Promise<Response>;
|
|
@@ -75,22 +75,28 @@ function cachedTextHeaders(contentType, options = {}) {
|
|
|
75
75
|
headers.set("vercel-cdn-cache-control", cdnCacheControl(options.cachePolicy));
|
|
76
76
|
headers.set("content-type", contentType);
|
|
77
77
|
headers.set("x-content-type-options", "nosniff");
|
|
78
|
-
if (options.etag) headers.set("etag", options.etag);
|
|
79
78
|
if (options.noindex) headers.set("x-robots-tag", "noindex");
|
|
80
79
|
return headers;
|
|
81
80
|
}
|
|
82
81
|
function createCachedTextResponse(options) {
|
|
83
82
|
const status = options.status ?? 200;
|
|
84
|
-
const method = options.method ??
|
|
83
|
+
const method = options.method ?? "GET";
|
|
85
84
|
const headers = cachedTextHeaders(options.contentType, options);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
85
|
+
return new Response(method === "HEAD" ? null : options.body, {
|
|
86
|
+
headers,
|
|
87
|
+
status
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/kernel/routes/metadata/canonical-url.ts
|
|
92
|
+
function canonicalUrl(manifest, path) {
|
|
93
|
+
const origin = manifest.site.url?.replace(/\/+$/, "");
|
|
94
|
+
if (!origin) return path;
|
|
95
|
+
try {
|
|
96
|
+
return new URL(path, `${origin}/`).toString();
|
|
97
|
+
} catch {
|
|
98
|
+
return path;
|
|
99
|
+
}
|
|
94
100
|
}
|
|
95
101
|
|
|
96
102
|
// src/kernel/routes/metadata/feed.ts
|
|
@@ -103,15 +109,6 @@ function pageTimestamp(page) {
|
|
|
103
109
|
const timestamp = Date.parse(value);
|
|
104
110
|
return Number.isNaN(timestamp) ? -Infinity : timestamp;
|
|
105
111
|
}
|
|
106
|
-
function canonicalUrl(manifest, path) {
|
|
107
|
-
const origin = manifest.site.url?.replace(/\/+$/, "");
|
|
108
|
-
if (!origin) return path;
|
|
109
|
-
try {
|
|
110
|
-
return new URL(path, `${origin}/`).toString();
|
|
111
|
-
} catch {
|
|
112
|
-
return path;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
112
|
function pageInScope(page, options) {
|
|
116
113
|
if (options.scope === "site" || !options.group) return true;
|
|
117
114
|
if (options.scope === "self") return page.group === options.group;
|
|
@@ -256,8 +253,7 @@ function createPrecomputedFeedRoute(output) {
|
|
|
256
253
|
return function GET() {
|
|
257
254
|
return createCachedTextResponse({
|
|
258
255
|
body: output.body,
|
|
259
|
-
contentType: output.contentType
|
|
260
|
-
etag: output.etag
|
|
256
|
+
contentType: output.contentType
|
|
261
257
|
});
|
|
262
258
|
};
|
|
263
259
|
}
|
|
@@ -102,22 +102,28 @@ function cachedTextHeaders(contentType, options = {}) {
|
|
|
102
102
|
headers.set("vercel-cdn-cache-control", cdnCacheControl(options.cachePolicy));
|
|
103
103
|
headers.set("content-type", contentType);
|
|
104
104
|
headers.set("x-content-type-options", "nosniff");
|
|
105
|
-
if (options.etag) headers.set("etag", options.etag);
|
|
106
105
|
if (options.noindex) headers.set("x-robots-tag", "noindex");
|
|
107
106
|
return headers;
|
|
108
107
|
}
|
|
109
108
|
function createCachedTextResponse(options) {
|
|
110
109
|
const status = options.status ?? 200;
|
|
111
|
-
const method = options.method ??
|
|
110
|
+
const method = options.method ?? "GET";
|
|
112
111
|
const headers = cachedTextHeaders(options.contentType, options);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
112
|
+
return new Response(method === "HEAD" ? null : options.body, {
|
|
113
|
+
headers,
|
|
114
|
+
status
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/kernel/routes/metadata/canonical-url.ts
|
|
119
|
+
function canonicalUrl(manifest, path) {
|
|
120
|
+
const origin = manifest.site.url?.replace(/\/+$/, "");
|
|
121
|
+
if (!origin) return path;
|
|
122
|
+
try {
|
|
123
|
+
return new URL(path, `${origin}/`).toString();
|
|
124
|
+
} catch {
|
|
125
|
+
return path;
|
|
126
|
+
}
|
|
121
127
|
}
|
|
122
128
|
|
|
123
129
|
// src/kernel/routes/metadata/llms.ts
|
|
@@ -146,10 +152,10 @@ async function llmsTxt(manifest, options = {}) {
|
|
|
146
152
|
const visiblePages = options.limit ? pages.slice(0, options.limit) : pages;
|
|
147
153
|
const lines = visiblePages.map((page) => {
|
|
148
154
|
const description = options.includeDescriptions && page.description ? ` - ${page.description}` : "";
|
|
149
|
-
return `- [${page.title}](${page.path})${description}`;
|
|
155
|
+
return `- [${page.title}](${canonicalUrl(manifest, page.path)})${description}`;
|
|
150
156
|
});
|
|
151
157
|
const linkLines = (options.links ?? []).map(
|
|
152
|
-
(link) => `- [${link.title}](${link.path})`
|
|
158
|
+
(link) => `- [${link.title}](${canonicalUrl(manifest, link.path)})`
|
|
153
159
|
);
|
|
154
160
|
const linkSection = linkLines.length ? `
|
|
155
161
|
|
|
@@ -175,8 +181,7 @@ function createPrecomputedLlmsTxtRoute(output) {
|
|
|
175
181
|
return function GET() {
|
|
176
182
|
return createCachedTextResponse({
|
|
177
183
|
body: output.body,
|
|
178
|
-
contentType: "text/plain; charset=utf-8"
|
|
179
|
-
etag: output.etag
|
|
184
|
+
contentType: "text/plain; charset=utf-8"
|
|
180
185
|
});
|
|
181
186
|
};
|
|
182
187
|
}
|
|
@@ -184,8 +189,8 @@ async function llmsFullTxt(manifest, artifacts, options = {}) {
|
|
|
184
189
|
const maxBytes = options.maxBytes ?? DEFAULT_LLMS_FULL_MAX_BYTES;
|
|
185
190
|
const header = `# ${manifest.site.name} Full Content
|
|
186
191
|
|
|
187
|
-
> LLMS index: /llms.txt
|
|
188
|
-
> Sitemap: /sitemap.md
|
|
192
|
+
> LLMS index: ${canonicalUrl(manifest, "/llms.txt")}
|
|
193
|
+
> Sitemap: ${canonicalUrl(manifest, "/sitemap.md")}
|
|
189
194
|
|
|
190
195
|
`;
|
|
191
196
|
const sections = [];
|
|
@@ -17,7 +17,6 @@ type LlmsFullRouteOptions = {
|
|
|
17
17
|
};
|
|
18
18
|
type PrecomputedLlmsTxtOutput = {
|
|
19
19
|
body: string;
|
|
20
|
-
etag?: string;
|
|
21
20
|
};
|
|
22
21
|
declare function createLlmsTxtRoute(manifest: ManifestLike, options?: LlmsRouteOptions): () => Promise<Response>;
|
|
23
22
|
declare function createPrecomputedLlmsTxtRoute(output: PrecomputedLlmsTxtOutput): () => Response;
|
|
@@ -17,7 +17,6 @@ type LlmsFullRouteOptions = {
|
|
|
17
17
|
};
|
|
18
18
|
type PrecomputedLlmsTxtOutput = {
|
|
19
19
|
body: string;
|
|
20
|
-
etag?: string;
|
|
21
20
|
};
|
|
22
21
|
declare function createLlmsTxtRoute(manifest: ManifestLike, options?: LlmsRouteOptions): () => Promise<Response>;
|
|
23
22
|
declare function createPrecomputedLlmsTxtRoute(output: PrecomputedLlmsTxtOutput): () => Response;
|
|
@@ -75,22 +75,28 @@ function cachedTextHeaders(contentType, options = {}) {
|
|
|
75
75
|
headers.set("vercel-cdn-cache-control", cdnCacheControl(options.cachePolicy));
|
|
76
76
|
headers.set("content-type", contentType);
|
|
77
77
|
headers.set("x-content-type-options", "nosniff");
|
|
78
|
-
if (options.etag) headers.set("etag", options.etag);
|
|
79
78
|
if (options.noindex) headers.set("x-robots-tag", "noindex");
|
|
80
79
|
return headers;
|
|
81
80
|
}
|
|
82
81
|
function createCachedTextResponse(options) {
|
|
83
82
|
const status = options.status ?? 200;
|
|
84
|
-
const method = options.method ??
|
|
83
|
+
const method = options.method ?? "GET";
|
|
85
84
|
const headers = cachedTextHeaders(options.contentType, options);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
85
|
+
return new Response(method === "HEAD" ? null : options.body, {
|
|
86
|
+
headers,
|
|
87
|
+
status
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/kernel/routes/metadata/canonical-url.ts
|
|
92
|
+
function canonicalUrl(manifest, path) {
|
|
93
|
+
const origin = manifest.site.url?.replace(/\/+$/, "");
|
|
94
|
+
if (!origin) return path;
|
|
95
|
+
try {
|
|
96
|
+
return new URL(path, `${origin}/`).toString();
|
|
97
|
+
} catch {
|
|
98
|
+
return path;
|
|
99
|
+
}
|
|
94
100
|
}
|
|
95
101
|
|
|
96
102
|
// src/kernel/routes/metadata/llms.ts
|
|
@@ -119,10 +125,10 @@ async function llmsTxt(manifest, options = {}) {
|
|
|
119
125
|
const visiblePages = options.limit ? pages.slice(0, options.limit) : pages;
|
|
120
126
|
const lines = visiblePages.map((page) => {
|
|
121
127
|
const description = options.includeDescriptions && page.description ? ` - ${page.description}` : "";
|
|
122
|
-
return `- [${page.title}](${page.path})${description}`;
|
|
128
|
+
return `- [${page.title}](${canonicalUrl(manifest, page.path)})${description}`;
|
|
123
129
|
});
|
|
124
130
|
const linkLines = (options.links ?? []).map(
|
|
125
|
-
(link) => `- [${link.title}](${link.path})`
|
|
131
|
+
(link) => `- [${link.title}](${canonicalUrl(manifest, link.path)})`
|
|
126
132
|
);
|
|
127
133
|
const linkSection = linkLines.length ? `
|
|
128
134
|
|
|
@@ -148,8 +154,7 @@ function createPrecomputedLlmsTxtRoute(output) {
|
|
|
148
154
|
return function GET() {
|
|
149
155
|
return createCachedTextResponse({
|
|
150
156
|
body: output.body,
|
|
151
|
-
contentType: "text/plain; charset=utf-8"
|
|
152
|
-
etag: output.etag
|
|
157
|
+
contentType: "text/plain; charset=utf-8"
|
|
153
158
|
});
|
|
154
159
|
};
|
|
155
160
|
}
|
|
@@ -157,8 +162,8 @@ async function llmsFullTxt(manifest, artifacts, options = {}) {
|
|
|
157
162
|
const maxBytes = options.maxBytes ?? DEFAULT_LLMS_FULL_MAX_BYTES;
|
|
158
163
|
const header = `# ${manifest.site.name} Full Content
|
|
159
164
|
|
|
160
|
-
> LLMS index: /llms.txt
|
|
161
|
-
> Sitemap: /sitemap.md
|
|
165
|
+
> LLMS index: ${canonicalUrl(manifest, "/llms.txt")}
|
|
166
|
+
> Sitemap: ${canonicalUrl(manifest, "/sitemap.md")}
|
|
162
167
|
|
|
163
168
|
`;
|
|
164
169
|
const sections = [];
|
|
@@ -99,22 +99,28 @@ function cachedTextHeaders(contentType, options = {}) {
|
|
|
99
99
|
headers.set("vercel-cdn-cache-control", cdnCacheControl(options.cachePolicy));
|
|
100
100
|
headers.set("content-type", contentType);
|
|
101
101
|
headers.set("x-content-type-options", "nosniff");
|
|
102
|
-
if (options.etag) headers.set("etag", options.etag);
|
|
103
102
|
if (options.noindex) headers.set("x-robots-tag", "noindex");
|
|
104
103
|
return headers;
|
|
105
104
|
}
|
|
106
105
|
function createCachedTextResponse(options) {
|
|
107
106
|
const status = options.status ?? 200;
|
|
108
|
-
const method = options.method ??
|
|
107
|
+
const method = options.method ?? "GET";
|
|
109
108
|
const headers = cachedTextHeaders(options.contentType, options);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
109
|
+
return new Response(method === "HEAD" ? null : options.body, {
|
|
110
|
+
headers,
|
|
111
|
+
status
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// src/kernel/routes/metadata/canonical-url.ts
|
|
116
|
+
function canonicalUrl(manifest, path) {
|
|
117
|
+
const origin = manifest.site.url?.replace(/\/+$/, "");
|
|
118
|
+
if (!origin) return path;
|
|
119
|
+
try {
|
|
120
|
+
return new URL(path, `${origin}/`).toString();
|
|
121
|
+
} catch {
|
|
122
|
+
return path;
|
|
123
|
+
}
|
|
118
124
|
}
|
|
119
125
|
|
|
120
126
|
// src/kernel/routes/metadata/robots.ts
|
|
@@ -122,7 +128,11 @@ async function robotsTxt(manifest) {
|
|
|
122
128
|
"use cache";
|
|
123
129
|
(0, import_cache.cacheLife)("page");
|
|
124
130
|
(0, import_cache.cacheTag)(tags.site(), tags.robots(), tags.manifest(manifest.contentHash));
|
|
125
|
-
|
|
131
|
+
const sitemapUrl = canonicalUrl(manifest, "/sitemap.xml");
|
|
132
|
+
return `User-agent: *
|
|
133
|
+
Allow: /
|
|
134
|
+
Sitemap: ${sitemapUrl}
|
|
135
|
+
`;
|
|
126
136
|
}
|
|
127
137
|
function createRobotsTxtRoute(manifest) {
|
|
128
138
|
return async function GET() {
|