@xexr/blog-platform 1.1.4 → 1.1.7
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/CHANGELOG.md +8 -0
- package/dist/app/blog/[slug]/page.tsx +10 -3
- package/dist/app/blog/page.tsx +12 -11
- package/dist/app/blog/series/[slug]/page.d.ts +28 -0
- package/dist/app/blog/series/[slug]/page.tsx +39 -0
- package/dist/app/blog/series/page.d.ts +24 -0
- package/dist/app/blog/series/page.tsx +28 -0
- package/dist/components/ArchiveStructuredData.tsx +61 -0
- package/dist/components/SeriesArchivePages.tsx +222 -0
- package/dist/components/SeriesNavigation.tsx +117 -0
- package/dist/components/TopicArchivePages.test.ts +52 -4
- package/dist/components/TopicArchivePages.tsx +14 -4
- package/dist/{index-Dr8HaKGm.d.mts → index-DsXjuPn4.d.mts} +45 -1
- package/dist/{index-Dr8HaKGm.d.ts → index-DsXjuPn4.d.ts} +45 -1
- package/dist/index.d.mts +86 -5
- package/dist/index.d.ts +86 -5
- package/dist/index.js +14 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +14 -14
- package/dist/index.mjs.map +1 -1
- package/dist/lib/related-articles.d.mts +3 -2
- package/dist/lib/related-articles.d.ts +3 -2
- package/dist/lib/series.ts +55 -0
- package/dist/lib/sitemap.ts +78 -46
- package/dist/lib/storage/drizzle.ts +9 -0
- package/dist/lib/structured-data.ts +10 -11
- package/dist/lib/urls.ts +7 -0
- package/dist/{sitemap-BmCh8BpN.d.ts → sitemap-B64QGT4l.d.ts} +11 -4
- package/dist/{sitemap-Dbl4CUGI.d.mts → sitemap-Dy7Q4ATM.d.mts} +11 -4
- package/dist/sitemap.d.mts +2 -2
- package/dist/sitemap.d.ts +2 -2
- package/dist/sitemap.js +5 -5
- package/dist/sitemap.js.map +1 -1
- package/dist/sitemap.mjs +5 -5
- package/dist/sitemap.mjs.map +1 -1
- package/dist/types/contracts.ts +61 -0
- package/dist/types/index.d.mts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/package.json +12 -13
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PublishedTopic, PublishedArticle } from '../index.mjs';
|
|
2
2
|
|
|
3
3
|
interface RelatedArticleCandidate {
|
|
4
4
|
readonly id: string;
|
|
@@ -14,4 +14,5 @@ interface RelatedFeaturedArticlesOptions {
|
|
|
14
14
|
declare function getRelatedFeaturedArticles<TArticle extends RelatedArticleCandidate>(currentArticle: RelatedArticleCandidate, articles: readonly TArticle[], options?: RelatedFeaturedArticlesOptions): TArticle[];
|
|
15
15
|
type RelatedPublishedArticle = PublishedArticle;
|
|
16
16
|
|
|
17
|
-
export {
|
|
17
|
+
export { getRelatedFeaturedArticles };
|
|
18
|
+
export type { RelatedArticleCandidate, RelatedFeaturedArticlesOptions, RelatedPublishedArticle };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PublishedTopic, PublishedArticle } from '../index.js';
|
|
2
2
|
|
|
3
3
|
interface RelatedArticleCandidate {
|
|
4
4
|
readonly id: string;
|
|
@@ -14,4 +14,5 @@ interface RelatedFeaturedArticlesOptions {
|
|
|
14
14
|
declare function getRelatedFeaturedArticles<TArticle extends RelatedArticleCandidate>(currentArticle: RelatedArticleCandidate, articles: readonly TArticle[], options?: RelatedFeaturedArticlesOptions): TArticle[];
|
|
15
15
|
type RelatedPublishedArticle = PublishedArticle;
|
|
16
16
|
|
|
17
|
-
export {
|
|
17
|
+
export { getRelatedFeaturedArticles };
|
|
18
|
+
export type { RelatedArticleCandidate, RelatedFeaturedArticlesOptions, RelatedPublishedArticle };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PublishedArticle,
|
|
3
|
+
PublishedSeriesContext,
|
|
4
|
+
} from "../types/contracts";
|
|
5
|
+
|
|
6
|
+
export interface SeriesArchive {
|
|
7
|
+
series: PublishedSeriesContext;
|
|
8
|
+
articles: PublishedArticle[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getAllSeries(
|
|
12
|
+
articles: PublishedArticle[],
|
|
13
|
+
): PublishedSeriesContext[] {
|
|
14
|
+
const seriesBySlug = new Map<string, PublishedSeriesContext>();
|
|
15
|
+
|
|
16
|
+
for (const article of articles) {
|
|
17
|
+
if (
|
|
18
|
+
article.series &&
|
|
19
|
+
(!seriesBySlug.has(article.series.slug) ||
|
|
20
|
+
article.series.articles.length >
|
|
21
|
+
(seriesBySlug.get(article.series.slug)?.articles.length ?? 0))
|
|
22
|
+
) {
|
|
23
|
+
seriesBySlug.set(article.series.slug, article.series);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return Array.from(seriesBySlug.values()).sort((a, b) =>
|
|
28
|
+
a.title.localeCompare(b.title),
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getSeriesArchive(
|
|
33
|
+
articles: PublishedArticle[],
|
|
34
|
+
seriesSlug: string,
|
|
35
|
+
): SeriesArchive | null {
|
|
36
|
+
const matchingArticles = articles
|
|
37
|
+
.filter((article) => article.series?.slug === seriesSlug)
|
|
38
|
+
.sort((a, b) => (a.series?.position ?? 0) - (b.series?.position ?? 0));
|
|
39
|
+
|
|
40
|
+
if (matchingArticles.length === 0) return null;
|
|
41
|
+
|
|
42
|
+
const series = matchingArticles[0]?.series;
|
|
43
|
+
if (!series) return null;
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
series,
|
|
47
|
+
articles: matchingArticles,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function getArticleSeriesNavigation(
|
|
52
|
+
article: PublishedArticle,
|
|
53
|
+
): PublishedSeriesContext | null {
|
|
54
|
+
return article.series ?? null;
|
|
55
|
+
}
|
package/dist/lib/sitemap.ts
CHANGED
|
@@ -36,8 +36,9 @@ import {
|
|
|
36
36
|
normalizeDomain,
|
|
37
37
|
type CanonicalUrlOptions,
|
|
38
38
|
} from "./seo";
|
|
39
|
+
import { getAllSeries } from "./series";
|
|
39
40
|
import { getAllTopics } from "./topics";
|
|
40
|
-
import { buildAuthorPath, buildBlogPath } from "./urls";
|
|
41
|
+
import { buildAuthorPath, buildBlogPath, buildSeriesPath } from "./urls";
|
|
41
42
|
|
|
42
43
|
/**
|
|
43
44
|
* Configuration options for sitemap generation.
|
|
@@ -52,10 +53,13 @@ export interface SitemapOptions {
|
|
|
52
53
|
/** Whether to include topic pages in sitemap */
|
|
53
54
|
includeTopicPages?: boolean;
|
|
54
55
|
|
|
55
|
-
/** Whether to include
|
|
56
|
+
/** Whether to include series pages in sitemap */
|
|
57
|
+
includeSeriesPages?: boolean;
|
|
58
|
+
|
|
59
|
+
/** @deprecated Pagination pages are never included. */
|
|
56
60
|
includePaginationPages?: boolean;
|
|
57
61
|
|
|
58
|
-
/**
|
|
62
|
+
/** @deprecated Pagination pages are never included. */
|
|
59
63
|
articlesPerPage?: number;
|
|
60
64
|
|
|
61
65
|
/** Maximum number of sitemap entries (defaults to 50,000 per Google guidelines) */
|
|
@@ -71,6 +75,8 @@ export interface SitemapOptions {
|
|
|
71
75
|
authorPages?: SitemapChangeFreq;
|
|
72
76
|
/** Change frequency for topic index and archive pages */
|
|
73
77
|
topicPages?: SitemapChangeFreq;
|
|
78
|
+
/** Change frequency for series index and archive pages */
|
|
79
|
+
seriesPages?: SitemapChangeFreq;
|
|
74
80
|
/** Change frequency for pagination pages */
|
|
75
81
|
paginationPages?: SitemapChangeFreq;
|
|
76
82
|
};
|
|
@@ -85,6 +91,8 @@ export interface SitemapOptions {
|
|
|
85
91
|
authorPages?: number;
|
|
86
92
|
/** Priority for topic index and archive pages (0.0-1.0) */
|
|
87
93
|
topicPages?: number;
|
|
94
|
+
/** Priority for series index and archive pages (0.0-1.0) */
|
|
95
|
+
seriesPages?: number;
|
|
88
96
|
/** Priority for pagination pages (0.0-1.0) */
|
|
89
97
|
paginationPages?: number;
|
|
90
98
|
};
|
|
@@ -204,8 +212,7 @@ export async function generateBlogSitemapEntries(
|
|
|
204
212
|
const {
|
|
205
213
|
includeAuthorPages = true,
|
|
206
214
|
includeTopicPages = true,
|
|
207
|
-
|
|
208
|
-
articlesPerPage = 10,
|
|
215
|
+
includeSeriesPages = true,
|
|
209
216
|
maxEntries = 50000,
|
|
210
217
|
changeFrequency = {},
|
|
211
218
|
priority = {},
|
|
@@ -221,6 +228,7 @@ export async function generateBlogSitemapEntries(
|
|
|
221
228
|
const sitemapAuthors =
|
|
222
229
|
authors.length > 0 ? authors : getAuthorsFromArticles(articles);
|
|
223
230
|
const sitemapTopics = getAllTopics([...articles]);
|
|
231
|
+
const sitemapSeries = getAllSeries([...articles]);
|
|
224
232
|
|
|
225
233
|
// Add blog home page
|
|
226
234
|
entries.push({
|
|
@@ -257,7 +265,9 @@ export async function generateBlogSitemapEntries(
|
|
|
257
265
|
if (entries.length >= maxEntries) return;
|
|
258
266
|
|
|
259
267
|
const topicArticles = articles.filter((article) =>
|
|
260
|
-
(article.topics ?? []).some(
|
|
268
|
+
(article.topics ?? []).some(
|
|
269
|
+
(candidate) => candidate.slug === topic.slug,
|
|
270
|
+
),
|
|
261
271
|
);
|
|
262
272
|
|
|
263
273
|
entries.push({
|
|
@@ -269,6 +279,32 @@ export async function generateBlogSitemapEntries(
|
|
|
269
279
|
});
|
|
270
280
|
}
|
|
271
281
|
|
|
282
|
+
if (includeSeriesPages && sitemapSeries.length > 0) {
|
|
283
|
+
if (entries.length < maxEntries) {
|
|
284
|
+
entries.push({
|
|
285
|
+
url: `${baseUrl}${buildBlogPath(pathPrefix, "series")}`,
|
|
286
|
+
lastModified: getLatestModificationDate(articles),
|
|
287
|
+
changeFrequency: changeFrequency.seriesPages ?? "weekly",
|
|
288
|
+
priority: priority.seriesPages ?? 0.6,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
sitemapSeries.forEach((series) => {
|
|
293
|
+
if (entries.length >= maxEntries) return;
|
|
294
|
+
|
|
295
|
+
const seriesArticles = articles.filter(
|
|
296
|
+
(article) => article.series?.slug === series.slug,
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
entries.push({
|
|
300
|
+
url: `${baseUrl}${buildSeriesPath(pathPrefix, series.slug)}`,
|
|
301
|
+
lastModified: getLatestModificationDate(seriesArticles),
|
|
302
|
+
changeFrequency: changeFrequency.seriesPages ?? "weekly",
|
|
303
|
+
priority: priority.seriesPages ?? 0.6,
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
272
308
|
// Add author pages if enabled
|
|
273
309
|
if (includeAuthorPages && sitemapAuthors.length > 0) {
|
|
274
310
|
if (entries.length < maxEntries) {
|
|
@@ -300,22 +336,6 @@ export async function generateBlogSitemapEntries(
|
|
|
300
336
|
});
|
|
301
337
|
}
|
|
302
338
|
|
|
303
|
-
// Add pagination pages if enabled
|
|
304
|
-
if (includePaginationPages && articles.length > articlesPerPage) {
|
|
305
|
-
const totalPages = Math.ceil(articles.length / articlesPerPage);
|
|
306
|
-
|
|
307
|
-
for (let page = 2; page <= totalPages; page++) {
|
|
308
|
-
if (entries.length >= maxEntries) break;
|
|
309
|
-
|
|
310
|
-
entries.push({
|
|
311
|
-
url: `${baseUrl}${pathPrefix}?page=${page}`,
|
|
312
|
-
lastModified: getLatestModificationDate(articles),
|
|
313
|
-
changeFrequency: changeFrequency.paginationPages ?? "daily",
|
|
314
|
-
priority: priority.paginationPages ?? 0.5,
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
339
|
return entries;
|
|
320
340
|
}
|
|
321
341
|
|
|
@@ -338,8 +358,7 @@ export function generateSitemap(
|
|
|
338
358
|
const {
|
|
339
359
|
includeAuthorPages = true,
|
|
340
360
|
includeTopicPages = true,
|
|
341
|
-
|
|
342
|
-
articlesPerPage = 10,
|
|
361
|
+
includeSeriesPages = true,
|
|
343
362
|
maxEntries = 50000,
|
|
344
363
|
changeFrequency = {},
|
|
345
364
|
priority = {},
|
|
@@ -356,6 +375,7 @@ export function generateSitemap(
|
|
|
356
375
|
const sitemapAuthors =
|
|
357
376
|
authors.length > 0 ? authors : getAuthorsFromArticles(articles);
|
|
358
377
|
const sitemapTopics = getAllTopics([...articles]);
|
|
378
|
+
const sitemapSeries = getAllSeries([...articles]);
|
|
359
379
|
|
|
360
380
|
// Calculate total potential entries to determine if truncation will occur
|
|
361
381
|
let potentialEntries = 1; // Home page
|
|
@@ -363,12 +383,12 @@ export function generateSitemap(
|
|
|
363
383
|
if (includeTopicPages && sitemapTopics.length > 0) {
|
|
364
384
|
potentialEntries += sitemapTopics.length + 1; // Topic index + topic pages
|
|
365
385
|
}
|
|
366
|
-
if (
|
|
367
|
-
potentialEntries +=
|
|
386
|
+
if (includeSeriesPages && sitemapSeries.length > 0) {
|
|
387
|
+
potentialEntries += sitemapSeries.length + 1; // Series index + series pages
|
|
368
388
|
}
|
|
369
|
-
if (
|
|
370
|
-
|
|
371
|
-
|
|
389
|
+
if (includeAuthorPages) {
|
|
390
|
+
potentialEntries +=
|
|
391
|
+
sitemapAuthors.length > 0 ? sitemapAuthors.length + 1 : 0; // Authors index + author pages
|
|
372
392
|
}
|
|
373
393
|
|
|
374
394
|
const willTruncate = potentialEntries > maxEntries;
|
|
@@ -408,7 +428,9 @@ export function generateSitemap(
|
|
|
408
428
|
if (entries.length >= maxEntries) return;
|
|
409
429
|
|
|
410
430
|
const topicArticles = articles.filter((article) =>
|
|
411
|
-
(article.topics ?? []).some(
|
|
431
|
+
(article.topics ?? []).some(
|
|
432
|
+
(candidate) => candidate.slug === topic.slug,
|
|
433
|
+
),
|
|
412
434
|
);
|
|
413
435
|
|
|
414
436
|
entries.push({
|
|
@@ -420,6 +442,32 @@ export function generateSitemap(
|
|
|
420
442
|
});
|
|
421
443
|
}
|
|
422
444
|
|
|
445
|
+
if (includeSeriesPages && sitemapSeries.length > 0) {
|
|
446
|
+
if (entries.length < maxEntries) {
|
|
447
|
+
entries.push({
|
|
448
|
+
loc: `${baseUrl}${buildBlogPath(pathPrefix, "series")}`,
|
|
449
|
+
lastmod: getLatestModificationDate(articles),
|
|
450
|
+
changefreq: changeFrequency.seriesPages ?? "weekly",
|
|
451
|
+
priority: priority.seriesPages ?? 0.6,
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
sitemapSeries.forEach((series) => {
|
|
456
|
+
if (entries.length >= maxEntries) return;
|
|
457
|
+
|
|
458
|
+
const seriesArticles = articles.filter(
|
|
459
|
+
(article) => article.series?.slug === series.slug,
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
entries.push({
|
|
463
|
+
loc: `${baseUrl}${buildSeriesPath(pathPrefix, series.slug)}`,
|
|
464
|
+
lastmod: getLatestModificationDate(seriesArticles),
|
|
465
|
+
changefreq: changeFrequency.seriesPages ?? "weekly",
|
|
466
|
+
priority: priority.seriesPages ?? 0.6,
|
|
467
|
+
});
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
|
|
423
471
|
// Add author pages if enabled
|
|
424
472
|
if (includeAuthorPages && sitemapAuthors.length > 0) {
|
|
425
473
|
if (entries.length < maxEntries) {
|
|
@@ -451,22 +499,6 @@ export function generateSitemap(
|
|
|
451
499
|
});
|
|
452
500
|
}
|
|
453
501
|
|
|
454
|
-
// Add pagination pages if enabled
|
|
455
|
-
if (includePaginationPages && articles.length > articlesPerPage) {
|
|
456
|
-
const totalPages = Math.ceil(articles.length / articlesPerPage);
|
|
457
|
-
|
|
458
|
-
for (let page = 2; page <= totalPages; page++) {
|
|
459
|
-
if (entries.length >= maxEntries) break;
|
|
460
|
-
|
|
461
|
-
entries.push({
|
|
462
|
-
loc: `${baseUrl}${pathPrefix}?page=${page}`,
|
|
463
|
-
lastmod: getLatestModificationDate(articles),
|
|
464
|
-
changefreq: changeFrequency.paginationPages ?? "daily",
|
|
465
|
-
priority: priority.paginationPages ?? 0.5,
|
|
466
|
-
});
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
502
|
return {
|
|
471
503
|
entries,
|
|
472
504
|
totalEntries: entries.length,
|
|
@@ -98,6 +98,7 @@ function toRow(article: PublishedArticle): ArticleRow {
|
|
|
98
98
|
metaJson: JSON.stringify({
|
|
99
99
|
...article.meta,
|
|
100
100
|
__bloggyTopics: article.topics ?? [],
|
|
101
|
+
__bloggySeries: article.series ?? null,
|
|
101
102
|
__bloggyCta: article.cta ?? null,
|
|
102
103
|
}),
|
|
103
104
|
version: article.version,
|
|
@@ -126,6 +127,7 @@ function fromRow(row: ArticleRow): PublishedArticle {
|
|
|
126
127
|
site: JSON.parse(row.siteJson) as PublishedSite,
|
|
127
128
|
cta: extractCta(row.metaJson),
|
|
128
129
|
topics: extractTopics(row.metaJson),
|
|
130
|
+
series: extractSeries(row.metaJson),
|
|
129
131
|
meta: extractMeta(row.metaJson),
|
|
130
132
|
version: row.version,
|
|
131
133
|
};
|
|
@@ -133,6 +135,7 @@ function fromRow(row: ArticleRow): PublishedArticle {
|
|
|
133
135
|
|
|
134
136
|
type StoredArticleMeta = PublishedArticle["meta"] & {
|
|
135
137
|
__bloggyTopics?: PublishedArticle["topics"];
|
|
138
|
+
__bloggySeries?: PublishedArticle["series"] | null;
|
|
136
139
|
__bloggyCta?: PublishedArticle["cta"] | null;
|
|
137
140
|
};
|
|
138
141
|
|
|
@@ -150,9 +153,15 @@ function extractCta(metaJson: string): PublishedArticle["cta"] {
|
|
|
150
153
|
return parsed.__bloggyCta ?? undefined;
|
|
151
154
|
}
|
|
152
155
|
|
|
156
|
+
function extractSeries(metaJson: string): PublishedArticle["series"] {
|
|
157
|
+
const parsed = parseStoredMeta(metaJson);
|
|
158
|
+
return parsed.__bloggySeries ?? undefined;
|
|
159
|
+
}
|
|
160
|
+
|
|
153
161
|
function extractMeta(metaJson: string): PublishedArticle["meta"] {
|
|
154
162
|
const parsed = parseStoredMeta(metaJson);
|
|
155
163
|
delete parsed.__bloggyTopics;
|
|
164
|
+
delete parsed.__bloggySeries;
|
|
156
165
|
delete parsed.__bloggyCta;
|
|
157
166
|
const meta = parsed;
|
|
158
167
|
return meta;
|
|
@@ -222,18 +222,18 @@ export function generateArticleStructuredData(
|
|
|
222
222
|
schema.inLanguage = language ?? article.site.locale;
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
225
|
+
const subjects = [
|
|
226
|
+
...(article.topics ?? []).map((topic) => topic.name),
|
|
227
|
+
...(article.series ? [article.series.title] : []),
|
|
228
|
+
];
|
|
229
|
+
if (subjects.length) {
|
|
230
|
+
schema.about = [...new Set(subjects)].map((name) => ({
|
|
228
231
|
"@type": "Thing" as const,
|
|
229
|
-
name
|
|
232
|
+
name,
|
|
230
233
|
}));
|
|
231
234
|
}
|
|
232
235
|
|
|
233
|
-
|
|
234
|
-
if (article.site.toneAdjectives.length > 0) {
|
|
235
|
-
schema.genre = [...article.site.toneAdjectives];
|
|
236
|
-
}
|
|
236
|
+
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
return schema;
|
|
@@ -288,9 +288,8 @@ export function generateAuthorStructuredData(
|
|
|
288
288
|
schema.additionalType = "GuestAuthor";
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
schema.knowsAbout = [...author.toneAdjectives];
|
|
291
|
+
if (author.expertise?.length) {
|
|
292
|
+
schema.knowsAbout = [...author.expertise];
|
|
294
293
|
}
|
|
295
294
|
|
|
296
295
|
return schema;
|
package/dist/lib/urls.ts
CHANGED
|
@@ -30,6 +30,13 @@ export function buildAuthorPath(
|
|
|
30
30
|
return buildBlogPath(basePath, `authors/${authorSlug}`);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
export function buildSeriesPath(
|
|
34
|
+
basePath: string | undefined,
|
|
35
|
+
seriesSlug: string,
|
|
36
|
+
): string {
|
|
37
|
+
return buildBlogPath(basePath, `series/${seriesSlug}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
33
40
|
export function getPathPrefixFromBlogUrl(blogUrl: string): string {
|
|
34
41
|
const parsed = new URL(blogUrl);
|
|
35
42
|
return normalizeBlogBasePath(parsed.pathname);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as PublishedArticle, b as PublishedSite, c as PublishedAuthor } from './index-
|
|
1
|
+
import { a as PublishedArticle, b as PublishedSite, c as PublishedAuthor } from './index-DsXjuPn4.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* SEO utilities for canonical URL generation and validation.
|
|
@@ -171,9 +171,11 @@ interface SitemapOptions {
|
|
|
171
171
|
includeAuthorPages?: boolean;
|
|
172
172
|
/** Whether to include topic pages in sitemap */
|
|
173
173
|
includeTopicPages?: boolean;
|
|
174
|
-
/** Whether to include
|
|
174
|
+
/** Whether to include series pages in sitemap */
|
|
175
|
+
includeSeriesPages?: boolean;
|
|
176
|
+
/** @deprecated Pagination pages are never included. */
|
|
175
177
|
includePaginationPages?: boolean;
|
|
176
|
-
/**
|
|
178
|
+
/** @deprecated Pagination pages are never included. */
|
|
177
179
|
articlesPerPage?: number;
|
|
178
180
|
/** Maximum number of sitemap entries (defaults to 50,000 per Google guidelines) */
|
|
179
181
|
maxEntries?: number;
|
|
@@ -187,6 +189,8 @@ interface SitemapOptions {
|
|
|
187
189
|
authorPages?: SitemapChangeFreq;
|
|
188
190
|
/** Change frequency for topic index and archive pages */
|
|
189
191
|
topicPages?: SitemapChangeFreq;
|
|
192
|
+
/** Change frequency for series index and archive pages */
|
|
193
|
+
seriesPages?: SitemapChangeFreq;
|
|
190
194
|
/** Change frequency for pagination pages */
|
|
191
195
|
paginationPages?: SitemapChangeFreq;
|
|
192
196
|
};
|
|
@@ -200,6 +204,8 @@ interface SitemapOptions {
|
|
|
200
204
|
authorPages?: number;
|
|
201
205
|
/** Priority for topic index and archive pages (0.0-1.0) */
|
|
202
206
|
topicPages?: number;
|
|
207
|
+
/** Priority for series index and archive pages (0.0-1.0) */
|
|
208
|
+
seriesPages?: number;
|
|
203
209
|
/** Priority for pagination pages (0.0-1.0) */
|
|
204
210
|
paginationPages?: number;
|
|
205
211
|
};
|
|
@@ -419,4 +425,5 @@ interface BlogSitemapConfig {
|
|
|
419
425
|
*/
|
|
420
426
|
declare function createDynamicBlogSitemap(config: BlogSitemapConfig): () => Promise<NextjsSitemapEntry[]>;
|
|
421
427
|
|
|
422
|
-
export {
|
|
428
|
+
export { createBlogSitemapRoute as d, createDynamicBlogSitemap as e, generateCanonicalUrl as f, generateBlogSitemapEntries as g, generateCanonicalUrlFromSlug as h, generateSitemap as i, generateSitemapIndex as j, getBaseUrl as k, isValidCanonicalUrl as l, normalizeSlug as m, normalizeDomain as n, sitemapToXml as s, updateArticleCanonicalUrl as u, validateSitemap as v };
|
|
429
|
+
export type { BlogSitemapConfig as B, CanonicalUrlOptions as C, NextjsSitemapEntry as N, SitemapChangeFreq as S, SitemapData as a, SitemapEntry as b, SitemapOptions as c };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as PublishedArticle, b as PublishedSite, c as PublishedAuthor } from './index-
|
|
1
|
+
import { a as PublishedArticle, b as PublishedSite, c as PublishedAuthor } from './index-DsXjuPn4.mjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* SEO utilities for canonical URL generation and validation.
|
|
@@ -171,9 +171,11 @@ interface SitemapOptions {
|
|
|
171
171
|
includeAuthorPages?: boolean;
|
|
172
172
|
/** Whether to include topic pages in sitemap */
|
|
173
173
|
includeTopicPages?: boolean;
|
|
174
|
-
/** Whether to include
|
|
174
|
+
/** Whether to include series pages in sitemap */
|
|
175
|
+
includeSeriesPages?: boolean;
|
|
176
|
+
/** @deprecated Pagination pages are never included. */
|
|
175
177
|
includePaginationPages?: boolean;
|
|
176
|
-
/**
|
|
178
|
+
/** @deprecated Pagination pages are never included. */
|
|
177
179
|
articlesPerPage?: number;
|
|
178
180
|
/** Maximum number of sitemap entries (defaults to 50,000 per Google guidelines) */
|
|
179
181
|
maxEntries?: number;
|
|
@@ -187,6 +189,8 @@ interface SitemapOptions {
|
|
|
187
189
|
authorPages?: SitemapChangeFreq;
|
|
188
190
|
/** Change frequency for topic index and archive pages */
|
|
189
191
|
topicPages?: SitemapChangeFreq;
|
|
192
|
+
/** Change frequency for series index and archive pages */
|
|
193
|
+
seriesPages?: SitemapChangeFreq;
|
|
190
194
|
/** Change frequency for pagination pages */
|
|
191
195
|
paginationPages?: SitemapChangeFreq;
|
|
192
196
|
};
|
|
@@ -200,6 +204,8 @@ interface SitemapOptions {
|
|
|
200
204
|
authorPages?: number;
|
|
201
205
|
/** Priority for topic index and archive pages (0.0-1.0) */
|
|
202
206
|
topicPages?: number;
|
|
207
|
+
/** Priority for series index and archive pages (0.0-1.0) */
|
|
208
|
+
seriesPages?: number;
|
|
203
209
|
/** Priority for pagination pages (0.0-1.0) */
|
|
204
210
|
paginationPages?: number;
|
|
205
211
|
};
|
|
@@ -419,4 +425,5 @@ interface BlogSitemapConfig {
|
|
|
419
425
|
*/
|
|
420
426
|
declare function createDynamicBlogSitemap(config: BlogSitemapConfig): () => Promise<NextjsSitemapEntry[]>;
|
|
421
427
|
|
|
422
|
-
export {
|
|
428
|
+
export { createBlogSitemapRoute as d, createDynamicBlogSitemap as e, generateCanonicalUrl as f, generateBlogSitemapEntries as g, generateCanonicalUrlFromSlug as h, generateSitemap as i, generateSitemapIndex as j, getBaseUrl as k, isValidCanonicalUrl as l, normalizeSlug as m, normalizeDomain as n, sitemapToXml as s, updateArticleCanonicalUrl as u, validateSitemap as v };
|
|
429
|
+
export type { BlogSitemapConfig as B, CanonicalUrlOptions as C, NextjsSitemapEntry as N, SitemapChangeFreq as S, SitemapData as a, SitemapEntry as b, SitemapOptions as c };
|
package/dist/sitemap.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import './index-
|
|
2
|
-
export { B as BlogSitemapConfig, N as NextjsSitemapEntry, S as SitemapChangeFreq, a as SitemapData, b as SitemapEntry, c as SitemapOptions, d as createBlogSitemapRoute, e as createDynamicBlogSitemap, g as generateBlogSitemapEntries, i as generateSitemap, j as generateSitemapIndex, s as sitemapToXml, v as validateSitemap } from './sitemap-
|
|
1
|
+
import './index-DsXjuPn4.mjs';
|
|
2
|
+
export { B as BlogSitemapConfig, N as NextjsSitemapEntry, S as SitemapChangeFreq, a as SitemapData, b as SitemapEntry, c as SitemapOptions, d as createBlogSitemapRoute, e as createDynamicBlogSitemap, g as generateBlogSitemapEntries, i as generateSitemap, j as generateSitemapIndex, s as sitemapToXml, v as validateSitemap } from './sitemap-Dy7Q4ATM.mjs';
|
package/dist/sitemap.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import './index-
|
|
2
|
-
export { B as BlogSitemapConfig, N as NextjsSitemapEntry, S as SitemapChangeFreq, a as SitemapData, b as SitemapEntry, c as SitemapOptions, d as createBlogSitemapRoute, e as createDynamicBlogSitemap, g as generateBlogSitemapEntries, i as generateSitemap, j as generateSitemapIndex, s as sitemapToXml, v as validateSitemap } from './sitemap-
|
|
1
|
+
import './index-DsXjuPn4.js';
|
|
2
|
+
export { B as BlogSitemapConfig, N as NextjsSitemapEntry, S as SitemapChangeFreq, a as SitemapData, b as SitemapEntry, c as SitemapOptions, d as createBlogSitemapRoute, e as createDynamicBlogSitemap, g as generateBlogSitemapEntries, i as generateSitemap, j as generateSitemapIndex, s as sitemapToXml, v as validateSitemap } from './sitemap-B64QGT4l.js';
|
package/dist/sitemap.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
'use strict';function
|
|
2
|
-
`)});return [
|
|
3
|
-
`)}function
|
|
4
|
-
`)});return [r,i,...n
|
|
5
|
-
`)}function
|
|
1
|
+
'use strict';function v(t){let e=new Map;for(let r of t)r.author?.slug&&e.set(r.author.slug,F(e.get(r.author.slug),r.author));return Array.from(e.values()).sort((r,i)=>r.name.localeCompare(i.name))}function F(t,e){return t?{...t,bio:t.bio??e.bio,avatarUrl:t.avatarUrl??e.avatarUrl,pronouns:t.pronouns??e.pronouns,socialLinks:t.socialLinks??e.socialLinks,toneAdjectives:t.toneAdjectives.length>0?t.toneAdjectives:e.toneAdjectives}:e}function w(t,e=true){let r=t.trim();return r=r.replace(/\/+$/,""),/^https?:\/\//.exec(r)||(r=`${e?"https":"http"}://${r}`),e&&r.startsWith("http://")&&(r=r.replace("http://","https://")),r}function M(t){let e=t.trim();if(!e)throw new Error("Slug cannot be empty");if(!/^[a-z0-9-]+$/.test(e))throw new Error("Slug can only contain lowercase letters, numbers, and hyphens");let i=e.replace(/-+/g,"-").replace(/^-+|-+$/g,"");if(!i)throw new Error("Slug cannot consist only of hyphens");return i}function T(t,e={}){let{forceHttps:r=true,removeTrailingSlash:i=true,domain:n=t.site.domain,pathPrefix:o="/blog"}=e;if(!n)throw new Error("Domain is required for canonical URL generation");if(!t.slug)throw new Error("Article slug is required for canonical URL generation");let u=w(n,r),l=M(t.slug),c=o.replace(/^(?!\/)/,"/").replace(/\/+$/,""),a=`${u}${c}/${l}`;return i&&(a=a.replace(/\/+$/,"")),a}function O(t){let e=new Map;for(let r of t)r.series&&(!e.has(r.series.slug)||r.series.articles.length>(e.get(r.series.slug)?.articles.length??0))&&e.set(r.series.slug,r.series);return Array.from(e.values()).sort((r,i)=>r.title.localeCompare(i.title))}function _(t){let e=new Map;for(let r of t)for(let i of r.topics??[])e.has(i.slug)||e.set(i.slug,i);return Array.from(e.values()).sort((r,i)=>r.name.localeCompare(i.name))}function L(t="/blog"){let e=t.trim();return !e||e==="/"?"":e.replace(/^(?!\/)/,"/").replace(/\/+$/,"")}function P(t,e){let r=L(t),i=e?.replace(/^\/+/,"").replace(/\/+$/,"");return i?`${r}/${i}`.replace(/\/+/g,"/"):r||"/"}function U(t,e){return P(t,`authors/${e}`)}function B(t,e){return P(t,`series/${e}`)}async function q(t,e,r=[],i={}){let{includeAuthorPages:n=true,includeTopicPages:o=true,includeSeriesPages:u=true,maxEntries:l=5e4,changeFrequency:c={},priority:a={}}=i,s=[],h=w(i.canonical?.domain??t.domain,i.canonical?.forceHttps??true),p=i.canonical?.pathPrefix??"/blog",$=r.length>0?r:v(e),b=_([...e]),y=O([...e]);return s.push({url:`${h}${p}`,lastModified:m(e),changeFrequency:c.homePage??"daily",priority:a.homePage??1}),e.forEach(g=>{if(s.length>=l)return;let f=T(g,i.canonical);s.push({url:f,lastModified:g.updatedAt,changeFrequency:c.articles??"monthly",priority:a.articles??.8});}),o&&b.length>0&&(s.length<l&&s.push({url:`${h}${P(p,"topics")}`,lastModified:m(e),changeFrequency:c.topicPages??"weekly",priority:a.topicPages??.6}),b.forEach(g=>{if(s.length>=l)return;let f=e.filter(S=>(S.topics??[]).some(d=>d.slug===g.slug));s.push({url:`${h}${P(p,`topics/${g.slug}`)}`,lastModified:m(f),changeFrequency:c.topicPages??"weekly",priority:a.topicPages??.6});})),u&&y.length>0&&(s.length<l&&s.push({url:`${h}${P(p,"series")}`,lastModified:m(e),changeFrequency:c.seriesPages??"weekly",priority:a.seriesPages??.6}),y.forEach(g=>{if(s.length>=l)return;let f=e.filter(S=>S.series?.slug===g.slug);s.push({url:`${h}${B(p,g.slug)}`,lastModified:m(f),changeFrequency:c.seriesPages??"weekly",priority:a.seriesPages??.6});})),n&&$.length>0&&(s.length<l&&s.push({url:`${h}${P(p,"authors")}`,lastModified:m(e),changeFrequency:c.authorPages??"weekly",priority:a.authorPages??.6}),$.forEach(g=>{if(s.length>=l)return;let f=`${h}${U(p,g.slug)}`,S=e.filter(A=>A.author?.id===g.id),d=m(S);s.push({url:f,lastModified:d,changeFrequency:c.authorPages??"weekly",priority:a.authorPages??.6});})),s}function K(t,e,r=[],i={}){let{includeAuthorPages:n=true,includeTopicPages:o=true,includeSeriesPages:u=true,maxEntries:l=5e4,changeFrequency:c={},priority:a={}}=i,s=[],h=w(i.canonical?.domain??t.domain,i.canonical?.forceHttps??true),p=i.canonical?.pathPrefix??"/blog",$=new Date().toISOString(),b=r.length>0?r:v(e),y=_([...e]),g=O([...e]),f=1;f+=e.length,o&&y.length>0&&(f+=y.length+1),u&&g.length>0&&(f+=g.length+1),n&&(f+=b.length>0?b.length+1:0);let S=f>l;return s.push({loc:`${h}${p}`,lastmod:m(e),changefreq:c.homePage??"daily",priority:a.homePage??1}),e.forEach(d=>{if(s.length>=l)return;let A=T(d,i.canonical);s.push({loc:A,lastmod:d.updatedAt,changefreq:c.articles??"monthly",priority:a.articles??.8});}),o&&y.length>0&&(s.length<l&&s.push({loc:`${h}${P(p,"topics")}`,lastmod:m(e),changefreq:c.topicPages??"weekly",priority:a.topicPages??.6}),y.forEach(d=>{if(s.length>=l)return;let A=e.filter(x=>(x.topics??[]).some(E=>E.slug===d.slug));s.push({loc:`${h}${P(p,`topics/${d.slug}`)}`,lastmod:m(A),changefreq:c.topicPages??"weekly",priority:a.topicPages??.6});})),u&&g.length>0&&(s.length<l&&s.push({loc:`${h}${P(p,"series")}`,lastmod:m(e),changefreq:c.seriesPages??"weekly",priority:a.seriesPages??.6}),g.forEach(d=>{if(s.length>=l)return;let A=e.filter(x=>x.series?.slug===d.slug);s.push({loc:`${h}${B(p,d.slug)}`,lastmod:m(A),changefreq:c.seriesPages??"weekly",priority:a.seriesPages??.6});})),n&&b.length>0&&(s.length<l&&s.push({loc:`${h}${P(p,"authors")}`,lastmod:m(e),changefreq:c.authorPages??"weekly",priority:a.authorPages??.6}),b.forEach(d=>{if(s.length>=l)return;let A=`${h}${U(p,d.slug)}`,x=e.filter(C=>C.author?.id===d.id),E=m(x);s.push({loc:A,lastmod:E,changefreq:c.authorPages??"weekly",priority:a.authorPages??.6});})),{entries:s,totalEntries:s.length,truncated:S,generatedAt:$,baseUrl:h}}function k(t){let e='<?xml version="1.0" encoding="UTF-8"?>',r='<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',i="</urlset>",n=t.entries.map(o=>{let u=[];return u.push(" <url>"),u.push(` <loc>${R(o.loc)}</loc>`),o.lastmod&&u.push(` <lastmod>${R(o.lastmod)}</lastmod>`),o.changefreq&&u.push(` <changefreq>${R(o.changefreq)}</changefreq>`),o.priority!==void 0&&u.push(` <priority>${o.priority.toFixed(1)}</priority>`),u.push(" </url>"),u.join(`
|
|
2
|
+
`)});return [e,r,...n,i].join(`
|
|
3
|
+
`)}function ee(t,e){let r='<?xml version="1.0" encoding="UTF-8"?>',i='<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',n="</sitemapindex>",o=t.map(u=>{let l=[];return l.push(" <sitemap>"),l.push(` <loc>${R(u.url)}</loc>`),u.lastmod&&l.push(` <lastmod>${R(u.lastmod)}</lastmod>`),l.push(" </sitemap>"),l.join(`
|
|
4
|
+
`)});return [r,i,...o,n].join(`
|
|
5
|
+
`)}function te(t){let e=[],r=[];t.entries.length===0?e.push("Sitemap must contain at least one URL"):t.entries.length>5e4&&e.push("Sitemap contains more than 50,000 URLs (Google limit)"),t.truncated&&r.push("Sitemap was truncated due to size limits"),t.entries.forEach((n,o)=>{try{new URL(n.loc);}catch{e.push(`Invalid URL at entry ${o+1}: ${n.loc}`);}n.priority!==void 0&&(n.priority<0||n.priority>1)&&e.push(`Invalid priority at entry ${o+1}: must be between 0.0 and 1.0`),n.lastmod&&!D(n.lastmod)&&e.push(`Invalid lastmod date at entry ${o+1}: ${n.lastmod}`),n.loc.length>2048&&r.push(`URL at entry ${o+1} exceeds 2048 character limit`);});let i=new Map;return t.entries.forEach(n=>{let o=i.get(n.loc)??0;i.set(n.loc,o+1);}),i.forEach((n,o)=>{n>1&&e.push(`Duplicate URL found: ${o} (appears ${n} times)`);}),{valid:e.length===0,errors:e,warnings:r}}function m(t){return t.length===0?new Date().toISOString():t.reduce((r,i)=>{let n=new Date(i.updatedAt);return n>r?n:r},new Date(t[0].updatedAt)).toISOString()}function re(t,e,r=[],i={}){return async function(){let u=(await q(t,e,r,i)).map(a=>({loc:a.url,lastmod:typeof a.lastModified=="string"?a.lastModified:a.lastModified?.toISOString(),changefreq:a.changeFrequency,priority:a.priority})),l={entries:u,totalEntries:u.length,generatedAt:new Date().toISOString(),baseUrl:w(i.canonical?.domain??t.domain,i.canonical?.forceHttps??true)},c=k(l);return new Response(c,{headers:{"Content-Type":"application/xml","Cache-Control":"public, max-age=3600, s-maxage=3600"}})}}function ie(t){return async function(){let r=[...t.staticRoutes??[],...t.getStaticRoutes?await t.getStaticRoutes():[]],i=await t.getSite(),n=await t.getArticles(),o=t.getAuthors?await t.getAuthors():[];return [...r,...await q(i,n,o,t.options)]}}function R(t){let e={"&":"&","<":"<",">":">",'"':""","'":"'"};return t.replace(/[&<>"']/g,r=>e[r]??r)}function D(t){try{let e=new Date(t),r=e.toISOString(),i=t.endsWith("Z")&&!t.includes(".")?t.replace("Z",".000Z"):t;return r===i&&!isNaN(e.getTime())}catch{return false}}exports.createBlogSitemapRoute=re;exports.createDynamicBlogSitemap=ie;exports.generateBlogSitemapEntries=q;exports.generateSitemap=K;exports.generateSitemapIndex=ee;exports.sitemapToXml=k;exports.validateSitemap=te;//# sourceMappingURL=sitemap.js.map
|
|
6
6
|
//# sourceMappingURL=sitemap.js.map
|