@publish.os/nextjs 0.0.1-alpha.3 → 0.0.1-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +3 -9
  2. package/dist/index.cjs +26 -23
  3. package/dist/index.js +26 -23
  4. package/dist/kernel/components/blocks/design-system-preview.cjs +18 -17
  5. package/dist/kernel/components/blocks/design-system-preview.js +18 -17
  6. package/dist/kernel/components/blocks/link-playground.cjs +3 -3
  7. package/dist/kernel/components/blocks/link-playground.js +3 -3
  8. package/dist/kernel/components/blocks/navbar.cjs +1 -1
  9. package/dist/kernel/components/blocks/navbar.js +1 -1
  10. package/dist/kernel/components/blocks/navigation-progress-playground.cjs +2 -2
  11. package/dist/kernel/components/blocks/navigation-progress-playground.js +2 -2
  12. package/dist/kernel/components/ui/button-group.cjs +2 -3
  13. package/dist/kernel/components/ui/button-group.d.cts +2 -2
  14. package/dist/kernel/components/ui/button-group.d.ts +2 -2
  15. package/dist/kernel/components/ui/button-group.js +2 -3
  16. package/dist/kernel/components/ui/button.cjs +4 -12
  17. package/dist/kernel/components/ui/button.d.cts +3 -4
  18. package/dist/kernel/components/ui/button.d.ts +3 -4
  19. package/dist/kernel/components/ui/button.js +4 -12
  20. package/dist/kernel/components/ui/card.d.cts +1 -1
  21. package/dist/kernel/components/ui/card.d.ts +1 -1
  22. package/dist/kernel/components/ui/tag.cjs +104 -0
  23. package/dist/kernel/components/ui/tag.d.cts +14 -0
  24. package/dist/kernel/components/ui/tag.d.ts +14 -0
  25. package/dist/kernel/components/ui/tag.js +76 -0
  26. package/dist/kernel/components/ui/text.d.cts +1 -1
  27. package/dist/kernel/components/ui/text.d.ts +1 -1
  28. package/dist/kernel/components/ui/theme-toggle.cjs +1 -1
  29. package/dist/kernel/components/ui/theme-toggle.js +1 -1
  30. package/dist/kernel/defaults.registry.cjs +4 -4
  31. package/dist/kernel/defaults.registry.d.cts +4 -4
  32. package/dist/kernel/defaults.registry.d.ts +4 -4
  33. package/dist/kernel/defaults.registry.js +4 -4
  34. package/dist/kernel/routes/agent/markdown.cjs +5 -11
  35. package/dist/kernel/routes/agent/markdown.d.cts +0 -1
  36. package/dist/kernel/routes/agent/markdown.d.ts +0 -1
  37. package/dist/kernel/routes/agent/markdown.js +5 -11
  38. package/dist/kernel/routes/metadata/feed.cjs +17 -21
  39. package/dist/kernel/routes/metadata/feed.d.cts +0 -1
  40. package/dist/kernel/routes/metadata/feed.d.ts +0 -1
  41. package/dist/kernel/routes/metadata/feed.js +17 -21
  42. package/dist/kernel/routes/metadata/llms.cjs +21 -16
  43. package/dist/kernel/routes/metadata/llms.d.cts +0 -1
  44. package/dist/kernel/routes/metadata/llms.d.ts +0 -1
  45. package/dist/kernel/routes/metadata/llms.js +21 -16
  46. package/dist/kernel/routes/metadata/robots.cjs +21 -11
  47. package/dist/kernel/routes/metadata/robots.js +21 -11
  48. package/dist/kernel/routes/metadata/sitemap.cjs +29 -19
  49. package/dist/kernel/routes/metadata/sitemap.js +29 -19
  50. package/package.json +28 -5
  51. package/release/package.json +23 -0
  52. package/release/pnpm-lock.yaml +1215 -0
  53. package/release/release.json +39 -0
  54. package/dist/kernel/components/ui/badge.cjs +0 -75
  55. package/dist/kernel/components/ui/badge.d.cts +0 -12
  56. package/dist/kernel/components/ui/badge.d.ts +0 -12
  57. package/dist/kernel/components/ui/badge.js +0 -47
@@ -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 ?? options.request?.method ?? "GET";
83
+ const method = options.method ?? "GET";
85
84
  const headers = cachedTextHeaders(options.contentType, options);
86
- const isNotModified = status === 200 && options.etag && options.request?.headers.get("if-none-match") === options.etag;
87
- return new Response(
88
- method === "HEAD" || isNotModified ? null : options.body,
89
- {
90
- headers,
91
- status: isNotModified ? 304 : status
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 ?? options.request?.method ?? "GET";
110
+ const method = options.method ?? "GET";
112
111
  const headers = cachedTextHeaders(options.contentType, options);
113
- const isNotModified = status === 200 && options.etag && options.request?.headers.get("if-none-match") === options.etag;
114
- return new Response(
115
- method === "HEAD" || isNotModified ? null : options.body,
116
- {
117
- headers,
118
- status: isNotModified ? 304 : status
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 ?? options.request?.method ?? "GET";
83
+ const method = options.method ?? "GET";
85
84
  const headers = cachedTextHeaders(options.contentType, options);
86
- const isNotModified = status === 200 && options.etag && options.request?.headers.get("if-none-match") === options.etag;
87
- return new Response(
88
- method === "HEAD" || isNotModified ? null : options.body,
89
- {
90
- headers,
91
- status: isNotModified ? 304 : status
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 ?? options.request?.method ?? "GET";
107
+ const method = options.method ?? "GET";
109
108
  const headers = cachedTextHeaders(options.contentType, options);
110
- const isNotModified = status === 200 && options.etag && options.request?.headers.get("if-none-match") === options.etag;
111
- return new Response(
112
- method === "HEAD" || isNotModified ? null : options.body,
113
- {
114
- headers,
115
- status: isNotModified ? 304 : status
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
- return "User-agent: *\nAllow: /\nSitemap: /sitemap.xml\n";
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() {
@@ -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 ?? options.request?.method ?? "GET";
83
+ const method = options.method ?? "GET";
85
84
  const headers = cachedTextHeaders(options.contentType, options);
86
- const isNotModified = status === 200 && options.etag && options.request?.headers.get("if-none-match") === options.etag;
87
- return new Response(
88
- method === "HEAD" || isNotModified ? null : options.body,
89
- {
90
- headers,
91
- status: isNotModified ? 304 : status
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/robots.ts
@@ -98,7 +104,11 @@ async function robotsTxt(manifest) {
98
104
  "use cache";
99
105
  cacheLife("page");
100
106
  cacheTag(tags.site(), tags.robots(), tags.manifest(manifest.contentHash));
101
- return "User-agent: *\nAllow: /\nSitemap: /sitemap.xml\n";
107
+ const sitemapUrl = canonicalUrl(manifest, "/sitemap.xml");
108
+ return `User-agent: *
109
+ Allow: /
110
+ Sitemap: ${sitemapUrl}
111
+ `;
102
112
  }
103
113
  function createRobotsTxtRoute(manifest) {
104
114
  return async function GET() {
@@ -100,22 +100,28 @@ function cachedTextHeaders(contentType, options = {}) {
100
100
  headers.set("vercel-cdn-cache-control", cdnCacheControl(options.cachePolicy));
101
101
  headers.set("content-type", contentType);
102
102
  headers.set("x-content-type-options", "nosniff");
103
- if (options.etag) headers.set("etag", options.etag);
104
103
  if (options.noindex) headers.set("x-robots-tag", "noindex");
105
104
  return headers;
106
105
  }
107
106
  function createCachedTextResponse(options) {
108
107
  const status = options.status ?? 200;
109
- const method = options.method ?? options.request?.method ?? "GET";
108
+ const method = options.method ?? "GET";
110
109
  const headers = cachedTextHeaders(options.contentType, options);
111
- const isNotModified = status === 200 && options.etag && options.request?.headers.get("if-none-match") === options.etag;
112
- return new Response(
113
- method === "HEAD" || isNotModified ? null : options.body,
114
- {
115
- headers,
116
- status: isNotModified ? 304 : status
117
- }
118
- );
110
+ return new Response(method === "HEAD" ? null : options.body, {
111
+ headers,
112
+ status
113
+ });
114
+ }
115
+
116
+ // src/kernel/routes/metadata/canonical-url.ts
117
+ function canonicalUrl(manifest, path) {
118
+ const origin = manifest.site.url?.replace(/\/+$/, "");
119
+ if (!origin) return path;
120
+ try {
121
+ return new URL(path, `${origin}/`).toString();
122
+ } catch {
123
+ return path;
124
+ }
119
125
  }
120
126
 
121
127
  // src/kernel/routes/metadata/sitemap.ts
@@ -132,7 +138,9 @@ async function sitemapXml(manifest) {
132
138
  ...manifest.pages.filter((page) => page.status === "published").map((page) => page.path)
133
139
  ];
134
140
  const uniqueUrls = [...new Set(urls)];
135
- const body = uniqueUrls.map((url) => ` <url><loc>${xmlEscape(url)}</loc></url>`).join("\n");
141
+ const body = uniqueUrls.map(
142
+ (url) => ` <url><loc>${xmlEscape(canonicalUrl(manifest, url))}</loc></url>`
143
+ ).join("\n");
136
144
  return `<?xml version="1.0" encoding="UTF-8"?>
137
145
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
138
146
  ${body}
@@ -148,22 +156,24 @@ function createSitemapXmlRoute(manifest) {
148
156
  };
149
157
  }
150
158
  async function sitemapMarkdown(manifest, artifactManifest) {
151
- const groups = Object.values(manifest.groups).sort((a, b) => a.path.localeCompare(b.path)).map((group) => `- [${group.title}](${group.path})`);
159
+ const groups = Object.values(manifest.groups).sort((a, b) => a.path.localeCompare(b.path)).map(
160
+ (group) => `- [${group.title}](${canonicalUrl(manifest, group.path)})`
161
+ );
152
162
  const pages = manifest.pages.filter((page) => page.status === "published").sort((a, b) => a.path.localeCompare(b.path)).map((page) => {
153
163
  const markdownPath = artifactManifest.byHtmlPath[page.path];
154
- const markdown = markdownPath ? ` ([Markdown](${markdownPath}))` : "";
155
- return `- [${page.title}](${page.path})${markdown}`;
164
+ const markdown = markdownPath ? ` ([Markdown](${canonicalUrl(manifest, markdownPath)}))` : "";
165
+ return `- [${page.title}](${canonicalUrl(manifest, page.path)})${markdown}`;
156
166
  });
157
167
  return [
158
168
  `# ${manifest.site.name} Sitemap`,
159
169
  "## Agent indexes",
160
- "- [LLMS index](/llms.txt)",
161
- "- [Full LLMS index](/llms-full.txt)",
162
- "- [XML sitemap](/sitemap.xml)",
170
+ `- [LLMS index](${canonicalUrl(manifest, "/llms.txt")})`,
171
+ `- [Full LLMS index](${canonicalUrl(manifest, "/llms-full.txt")})`,
172
+ `- [XML sitemap](${canonicalUrl(manifest, "/sitemap.xml")})`,
163
173
  "## Sections",
164
- groups.length ? groups.join("\n") : "- [Home](/)",
174
+ groups.length ? groups.join("\n") : `- [Home](${canonicalUrl(manifest, "/")})`,
165
175
  "## Pages",
166
- pages.length ? pages.join("\n") : "- [Home](/)",
176
+ pages.length ? pages.join("\n") : `- [Home](${canonicalUrl(manifest, "/")})`,
167
177
  ""
168
178
  ].join("\n\n");
169
179
  }
@@ -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 ?? options.request?.method ?? "GET";
83
+ const method = options.method ?? "GET";
85
84
  const headers = cachedTextHeaders(options.contentType, options);
86
- const isNotModified = status === 200 && options.etag && options.request?.headers.get("if-none-match") === options.etag;
87
- return new Response(
88
- method === "HEAD" || isNotModified ? null : options.body,
89
- {
90
- headers,
91
- status: isNotModified ? 304 : status
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/sitemap.ts
@@ -107,7 +113,9 @@ async function sitemapXml(manifest) {
107
113
  ...manifest.pages.filter((page) => page.status === "published").map((page) => page.path)
108
114
  ];
109
115
  const uniqueUrls = [...new Set(urls)];
110
- const body = uniqueUrls.map((url) => ` <url><loc>${xmlEscape(url)}</loc></url>`).join("\n");
116
+ const body = uniqueUrls.map(
117
+ (url) => ` <url><loc>${xmlEscape(canonicalUrl(manifest, url))}</loc></url>`
118
+ ).join("\n");
111
119
  return `<?xml version="1.0" encoding="UTF-8"?>
112
120
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
113
121
  ${body}
@@ -123,22 +131,24 @@ function createSitemapXmlRoute(manifest) {
123
131
  };
124
132
  }
125
133
  async function sitemapMarkdown(manifest, artifactManifest) {
126
- const groups = Object.values(manifest.groups).sort((a, b) => a.path.localeCompare(b.path)).map((group) => `- [${group.title}](${group.path})`);
134
+ const groups = Object.values(manifest.groups).sort((a, b) => a.path.localeCompare(b.path)).map(
135
+ (group) => `- [${group.title}](${canonicalUrl(manifest, group.path)})`
136
+ );
127
137
  const pages = manifest.pages.filter((page) => page.status === "published").sort((a, b) => a.path.localeCompare(b.path)).map((page) => {
128
138
  const markdownPath = artifactManifest.byHtmlPath[page.path];
129
- const markdown = markdownPath ? ` ([Markdown](${markdownPath}))` : "";
130
- return `- [${page.title}](${page.path})${markdown}`;
139
+ const markdown = markdownPath ? ` ([Markdown](${canonicalUrl(manifest, markdownPath)}))` : "";
140
+ return `- [${page.title}](${canonicalUrl(manifest, page.path)})${markdown}`;
131
141
  });
132
142
  return [
133
143
  `# ${manifest.site.name} Sitemap`,
134
144
  "## Agent indexes",
135
- "- [LLMS index](/llms.txt)",
136
- "- [Full LLMS index](/llms-full.txt)",
137
- "- [XML sitemap](/sitemap.xml)",
145
+ `- [LLMS index](${canonicalUrl(manifest, "/llms.txt")})`,
146
+ `- [Full LLMS index](${canonicalUrl(manifest, "/llms-full.txt")})`,
147
+ `- [XML sitemap](${canonicalUrl(manifest, "/sitemap.xml")})`,
138
148
  "## Sections",
139
- groups.length ? groups.join("\n") : "- [Home](/)",
149
+ groups.length ? groups.join("\n") : `- [Home](${canonicalUrl(manifest, "/")})`,
140
150
  "## Pages",
141
- pages.length ? pages.join("\n") : "- [Home](/)",
151
+ pages.length ? pages.join("\n") : `- [Home](${canonicalUrl(manifest, "/")})`,
142
152
  ""
143
153
  ].join("\n\n");
144
154
  }
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@publish.os/nextjs",
3
- "version": "0.0.1-alpha.3",
3
+ "version": "0.0.1-alpha.5",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
+ "engines": {
7
+ "node": ">=22.13"
8
+ },
6
9
  "repository": {
7
10
  "type": "git",
8
11
  "url": "git+https://github.com/PublishOS/publishOS.git",
@@ -10,6 +13,7 @@
10
13
  },
11
14
  "files": [
12
15
  "dist/**",
16
+ "release/**",
13
17
  "package.json",
14
18
  "README.md"
15
19
  ],
@@ -54,6 +58,26 @@
54
58
  "types": "./dist/kernel/defaults.registry.d.ts",
55
59
  "default": "./dist/kernel/defaults.registry.js"
56
60
  },
61
+ "./kernel/routes/agent/*": {
62
+ "types": "./dist/kernel/routes/agent/*.d.ts",
63
+ "default": "./dist/kernel/routes/agent/*.js"
64
+ },
65
+ "./kernel/routes/layouts/*": {
66
+ "types": "./dist/kernel/routes/layouts/*.d.ts",
67
+ "default": "./dist/kernel/routes/layouts/*.js"
68
+ },
69
+ "./kernel/routes/metadata/*": {
70
+ "types": "./dist/kernel/routes/metadata/*.d.ts",
71
+ "default": "./dist/kernel/routes/metadata/*.js"
72
+ },
73
+ "./kernel/routes/pages/*": {
74
+ "types": "./dist/kernel/routes/pages/*.d.ts",
75
+ "default": "./dist/kernel/routes/pages/*.js"
76
+ },
77
+ "./kernel/routes/*": {
78
+ "types": "./dist/kernel/routes/*.d.ts",
79
+ "default": "./dist/kernel/routes/*.js"
80
+ },
57
81
  "./kernel/*": {
58
82
  "types": "./dist/kernel/*.d.ts",
59
83
  "default": "./dist/kernel/*.js"
@@ -86,11 +110,10 @@
86
110
  "react": "^19.2.0",
87
111
  "react-medium-image-zoom": "^5.4.8",
88
112
  "remark-gfm": "^4.0.1",
89
- "tailwind-merge": "^3.0.0",
90
- "tslib": "^2.8.1"
113
+ "tailwind-merge": "^3.0.0"
91
114
  },
92
115
  "peerDependencies": {
93
- "next": ">=16.0.0",
116
+ "next": ">=16.2.11",
94
117
  "react": ">=19.0.0",
95
118
  "react-dom": ">=19.0.0",
96
119
  "typescript": ">=5.0.0"
@@ -99,4 +122,4 @@
99
122
  "registry": "https://registry.npmjs.org",
100
123
  "access": "public"
101
124
  }
102
- }
125
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "publish-os-nextjs-release-base",
3
+ "private": true,
4
+ "type": "module",
5
+ "engines": {
6
+ "node": ">=22.13"
7
+ },
8
+ "dependencies": {
9
+ "@base-ui/react": "1.5.0",
10
+ "@tailwindcss/postcss": "4.3.0",
11
+ "@types/node": "22.19.17",
12
+ "@types/react": "19.2.2",
13
+ "@types/react-dom": "19.2.2",
14
+ "class-variance-authority": "0.7.1",
15
+ "next": "16.2.11",
16
+ "next-themes": "0.4.6",
17
+ "pagefind": "1.5.2",
18
+ "react": "19.2.0",
19
+ "react-dom": "19.2.0",
20
+ "tailwindcss": "4.3.0",
21
+ "typescript": "5.9.2"
22
+ }
23
+ }