@xexr/blog-platform 1.0.14 → 1.0.16

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.
@@ -0,0 +1,17 @@
1
+ import { P as PublishedTopic, a as PublishedArticle } from '../index-Dr8HaKGm.mjs';
2
+
3
+ interface RelatedArticleCandidate {
4
+ readonly id: string;
5
+ readonly slug: string;
6
+ readonly title?: string;
7
+ readonly publishedAt: string;
8
+ readonly updatedAt?: string;
9
+ readonly topics?: readonly PublishedTopic[];
10
+ }
11
+ interface RelatedFeaturedArticlesOptions {
12
+ readonly limit?: number;
13
+ }
14
+ declare function getRelatedFeaturedArticles<TArticle extends RelatedArticleCandidate>(currentArticle: RelatedArticleCandidate, articles: readonly TArticle[], options?: RelatedFeaturedArticlesOptions): TArticle[];
15
+ type RelatedPublishedArticle = PublishedArticle;
16
+
17
+ export { type RelatedArticleCandidate, type RelatedFeaturedArticlesOptions, type RelatedPublishedArticle, getRelatedFeaturedArticles };
@@ -0,0 +1,17 @@
1
+ import { P as PublishedTopic, a as PublishedArticle } from '../index-Dr8HaKGm.js';
2
+
3
+ interface RelatedArticleCandidate {
4
+ readonly id: string;
5
+ readonly slug: string;
6
+ readonly title?: string;
7
+ readonly publishedAt: string;
8
+ readonly updatedAt?: string;
9
+ readonly topics?: readonly PublishedTopic[];
10
+ }
11
+ interface RelatedFeaturedArticlesOptions {
12
+ readonly limit?: number;
13
+ }
14
+ declare function getRelatedFeaturedArticles<TArticle extends RelatedArticleCandidate>(currentArticle: RelatedArticleCandidate, articles: readonly TArticle[], options?: RelatedFeaturedArticlesOptions): TArticle[];
15
+ type RelatedPublishedArticle = PublishedArticle;
16
+
17
+ export { type RelatedArticleCandidate, type RelatedFeaturedArticlesOptions, type RelatedPublishedArticle, getRelatedFeaturedArticles };
@@ -0,0 +1,2 @@
1
+ 'use strict';function m(e,i,r={}){let n=r.limit??3;if(n<=0)return [];let s=o(e),t=T(e,i),d=(l,c)=>u(c)-u(l)||A(l).localeCompare(A(c)),a=s?t.filter(l=>o(l)===s).sort(d):[],p=new Set(a.map(l=>l.id)),g=t.filter(l=>!p.has(l.id)).sort(d);return [...a,...g].slice(0,n)}function T(e,i){let r=new Set,n=new Set,s=[];for(let t of i)t.id===e.id||t.slug===e.slug||r.has(t.id)||n.has(t.slug)||(r.add(t.id),n.add(t.slug),s.push(t));return s}function o(e){let i=e.topics??[];return i.find(r=>r.isPrimary)?.slug??i[0]?.slug??null}function u(e){let i=Date.parse(e.publishedAt??e.updatedAt??"");return Number.isFinite(i)?i:0}function A(e){return e.title??e.slug}exports.getRelatedFeaturedArticles=m;//# sourceMappingURL=related-articles.js.map
2
+ //# sourceMappingURL=related-articles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/related-articles.ts"],"names":["getRelatedFeaturedArticles","currentArticle","articles","options","limit","currentPrimaryTopicSlug","getPrimaryTopicSlug","eligibleArticles","getUniqueEligibleArticles","byNewest","a","b","getArticleTime","getArticleTitle","samePrimaryTopic","article","sameTopicIds","fallbackArticles","seenIds","seenSlugs","uniqueArticles","topics","topic","timestamp"],"mappings":"aAiBO,SAASA,CAAAA,CAGdC,EACAC,CAAAA,CACAC,CAAAA,CAA0C,EAAC,CAC/B,CACZ,IAAMC,CAAAA,CAAQD,CAAAA,CAAQ,KAAA,EAAS,EAC/B,GAAIC,CAAAA,EAAS,EAAG,OAAO,GAEvB,IAAMC,CAAAA,CAA0BC,CAAAA,CAAoBL,CAAc,CAAA,CAC5DM,CAAAA,CAAmBC,EAA0BP,CAAAA,CAAgBC,CAAQ,EACrEO,CAAAA,CAAW,CAACC,EAAaC,CAAAA,GAC7BC,CAAAA,CAAeD,CAAC,CAAA,CAAIC,CAAAA,CAAeF,CAAC,GAAKG,CAAAA,CAAgBH,CAAC,EAAE,aAAA,CAAcG,CAAAA,CAAgBF,CAAC,CAAC,CAAA,CAExFG,CAAAA,CAAmBT,CAAAA,CACrBE,CAAAA,CACG,MAAA,CACEQ,GAAYT,CAAAA,CAAoBS,CAAO,IAAMV,CAChD,CAAA,CACC,KAAKI,CAAQ,CAAA,CAChB,EAAC,CAECO,CAAAA,CAAe,IAAI,IAAIF,CAAAA,CAAiB,GAAA,CAAKC,GAAYA,CAAAA,CAAQ,EAAE,CAAC,CAAA,CACpEE,CAAAA,CAAmBV,CAAAA,CACtB,MAAA,CAAQQ,CAAAA,EAAY,CAACC,EAAa,GAAA,CAAID,CAAAA,CAAQ,EAAE,CAAC,CAAA,CACjD,KAAKN,CAAQ,CAAA,CAEhB,OAAO,CAAC,GAAGK,CAAAA,CAAkB,GAAGG,CAAgB,CAAA,CAAE,MAAM,CAAA,CAAGb,CAAK,CAClE,CAEA,SAASI,CAAAA,CACPP,CAAAA,CACAC,CAAAA,CACY,CACZ,IAAMgB,CAAAA,CAAU,IAAI,IACdC,CAAAA,CAAY,IAAI,IAChBC,CAAAA,CAA6B,EAAC,CAEpC,IAAA,IAAWL,CAAAA,IAAWb,CAAAA,CAChBa,EAAQ,EAAA,GAAOd,CAAAA,CAAe,IAAMc,CAAAA,CAAQ,IAAA,GAASd,EAAe,IAAA,EAIpEiB,CAAAA,CAAQ,GAAA,CAAIH,CAAAA,CAAQ,EAAE,CAAA,EAAKI,EAAU,GAAA,CAAIJ,CAAAA,CAAQ,IAAI,CAAA,GAIzDG,CAAAA,CAAQ,IAAIH,CAAAA,CAAQ,EAAE,CAAA,CACtBI,CAAAA,CAAU,GAAA,CAAIJ,CAAAA,CAAQ,IAAI,CAAA,CAC1BK,CAAAA,CAAe,KAAKL,CAAO,CAAA,CAAA,CAG7B,OAAOK,CACT,CAEA,SAASd,CAAAA,CAAoBS,CAAAA,CAAiD,CAC5E,IAAMM,CAAAA,CAASN,CAAAA,CAAQ,QAAU,EAAC,CAClC,OACEM,CAAAA,CAAO,IAAA,CAAMC,CAAAA,EAAUA,CAAAA,CAAM,SAAS,CAAA,EAAG,MAAQD,CAAAA,CAAO,CAAC,GAAG,IAAA,EAAQ,IAExE,CAEA,SAAST,CAAAA,CAAeG,CAAAA,CAA0C,CAChE,IAAMQ,CAAAA,CAAY,KAAK,KAAA,CAAMR,CAAAA,CAAQ,aAAeA,CAAAA,CAAQ,SAAA,EAAa,EAAE,CAAA,CAC3E,OAAO,MAAA,CAAO,QAAA,CAASQ,CAAS,CAAA,CAAIA,EAAY,CAClD,CAEA,SAASV,CAAAA,CAAgBE,CAAAA,CAA0C,CACjE,OAAOA,CAAAA,CAAQ,KAAA,EAASA,CAAAA,CAAQ,IAClC","file":"related-articles.js","sourcesContent":["import type { PublishedArticle, PublishedTopic } from \"../types/contracts\";\n\nexport interface RelatedArticleCandidate {\n readonly id: string;\n readonly slug: string;\n readonly title?: string;\n readonly publishedAt: string;\n readonly updatedAt?: string;\n readonly topics?: readonly PublishedTopic[];\n}\n\nexport interface RelatedFeaturedArticlesOptions {\n readonly limit?: number;\n}\n\nconst DEFAULT_RELATED_ARTICLE_LIMIT = 3;\n\nexport function getRelatedFeaturedArticles<\n TArticle extends RelatedArticleCandidate,\n>(\n currentArticle: RelatedArticleCandidate,\n articles: readonly TArticle[],\n options: RelatedFeaturedArticlesOptions = {},\n): TArticle[] {\n const limit = options.limit ?? DEFAULT_RELATED_ARTICLE_LIMIT;\n if (limit <= 0) return [];\n\n const currentPrimaryTopicSlug = getPrimaryTopicSlug(currentArticle);\n const eligibleArticles = getUniqueEligibleArticles(currentArticle, articles);\n const byNewest = (a: TArticle, b: TArticle) =>\n getArticleTime(b) - getArticleTime(a) || getArticleTitle(a).localeCompare(getArticleTitle(b));\n\n const samePrimaryTopic = currentPrimaryTopicSlug\n ? eligibleArticles\n .filter(\n (article) => getPrimaryTopicSlug(article) === currentPrimaryTopicSlug,\n )\n .sort(byNewest)\n : [];\n\n const sameTopicIds = new Set(samePrimaryTopic.map((article) => article.id));\n const fallbackArticles = eligibleArticles\n .filter((article) => !sameTopicIds.has(article.id))\n .sort(byNewest);\n\n return [...samePrimaryTopic, ...fallbackArticles].slice(0, limit);\n}\n\nfunction getUniqueEligibleArticles<TArticle extends RelatedArticleCandidate>(\n currentArticle: RelatedArticleCandidate,\n articles: readonly TArticle[],\n): TArticle[] {\n const seenIds = new Set<string>();\n const seenSlugs = new Set<string>();\n const uniqueArticles: TArticle[] = [];\n\n for (const article of articles) {\n if (article.id === currentArticle.id || article.slug === currentArticle.slug) {\n continue;\n }\n\n if (seenIds.has(article.id) || seenSlugs.has(article.slug)) {\n continue;\n }\n\n seenIds.add(article.id);\n seenSlugs.add(article.slug);\n uniqueArticles.push(article);\n }\n\n return uniqueArticles;\n}\n\nfunction getPrimaryTopicSlug(article: RelatedArticleCandidate): string | null {\n const topics = article.topics ?? [];\n return (\n topics.find((topic) => topic.isPrimary)?.slug ?? topics[0]?.slug ?? null\n );\n}\n\nfunction getArticleTime(article: RelatedArticleCandidate): number {\n const timestamp = Date.parse(article.publishedAt ?? article.updatedAt ?? \"\");\n return Number.isFinite(timestamp) ? timestamp : 0;\n}\n\nfunction getArticleTitle(article: RelatedArticleCandidate): string {\n return article.title ?? article.slug;\n}\n\nexport type RelatedPublishedArticle = PublishedArticle;\n"]}
@@ -0,0 +1,2 @@
1
+ function m(e,i,r={}){let n=r.limit??3;if(n<=0)return [];let s=o(e),t=T(e,i),d=(l,c)=>u(c)-u(l)||A(l).localeCompare(A(c)),a=s?t.filter(l=>o(l)===s).sort(d):[],p=new Set(a.map(l=>l.id)),g=t.filter(l=>!p.has(l.id)).sort(d);return [...a,...g].slice(0,n)}function T(e,i){let r=new Set,n=new Set,s=[];for(let t of i)t.id===e.id||t.slug===e.slug||r.has(t.id)||n.has(t.slug)||(r.add(t.id),n.add(t.slug),s.push(t));return s}function o(e){let i=e.topics??[];return i.find(r=>r.isPrimary)?.slug??i[0]?.slug??null}function u(e){let i=Date.parse(e.publishedAt??e.updatedAt??"");return Number.isFinite(i)?i:0}function A(e){return e.title??e.slug}export{m as getRelatedFeaturedArticles};//# sourceMappingURL=related-articles.mjs.map
2
+ //# sourceMappingURL=related-articles.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/related-articles.ts"],"names":["getRelatedFeaturedArticles","currentArticle","articles","options","limit","currentPrimaryTopicSlug","getPrimaryTopicSlug","eligibleArticles","getUniqueEligibleArticles","byNewest","a","b","getArticleTime","getArticleTitle","samePrimaryTopic","article","sameTopicIds","fallbackArticles","seenIds","seenSlugs","uniqueArticles","topics","topic","timestamp"],"mappings":"AAiBO,SAASA,CAAAA,CAGdC,EACAC,CAAAA,CACAC,CAAAA,CAA0C,EAAC,CAC/B,CACZ,IAAMC,CAAAA,CAAQD,CAAAA,CAAQ,KAAA,EAAS,EAC/B,GAAIC,CAAAA,EAAS,EAAG,OAAO,GAEvB,IAAMC,CAAAA,CAA0BC,CAAAA,CAAoBL,CAAc,CAAA,CAC5DM,CAAAA,CAAmBC,EAA0BP,CAAAA,CAAgBC,CAAQ,EACrEO,CAAAA,CAAW,CAACC,EAAaC,CAAAA,GAC7BC,CAAAA,CAAeD,CAAC,CAAA,CAAIC,CAAAA,CAAeF,CAAC,GAAKG,CAAAA,CAAgBH,CAAC,EAAE,aAAA,CAAcG,CAAAA,CAAgBF,CAAC,CAAC,CAAA,CAExFG,CAAAA,CAAmBT,CAAAA,CACrBE,CAAAA,CACG,MAAA,CACEQ,GAAYT,CAAAA,CAAoBS,CAAO,IAAMV,CAChD,CAAA,CACC,KAAKI,CAAQ,CAAA,CAChB,EAAC,CAECO,CAAAA,CAAe,IAAI,IAAIF,CAAAA,CAAiB,GAAA,CAAKC,GAAYA,CAAAA,CAAQ,EAAE,CAAC,CAAA,CACpEE,CAAAA,CAAmBV,CAAAA,CACtB,MAAA,CAAQQ,CAAAA,EAAY,CAACC,EAAa,GAAA,CAAID,CAAAA,CAAQ,EAAE,CAAC,CAAA,CACjD,KAAKN,CAAQ,CAAA,CAEhB,OAAO,CAAC,GAAGK,CAAAA,CAAkB,GAAGG,CAAgB,CAAA,CAAE,MAAM,CAAA,CAAGb,CAAK,CAClE,CAEA,SAASI,CAAAA,CACPP,CAAAA,CACAC,CAAAA,CACY,CACZ,IAAMgB,CAAAA,CAAU,IAAI,IACdC,CAAAA,CAAY,IAAI,IAChBC,CAAAA,CAA6B,EAAC,CAEpC,IAAA,IAAWL,CAAAA,IAAWb,CAAAA,CAChBa,EAAQ,EAAA,GAAOd,CAAAA,CAAe,IAAMc,CAAAA,CAAQ,IAAA,GAASd,EAAe,IAAA,EAIpEiB,CAAAA,CAAQ,GAAA,CAAIH,CAAAA,CAAQ,EAAE,CAAA,EAAKI,EAAU,GAAA,CAAIJ,CAAAA,CAAQ,IAAI,CAAA,GAIzDG,CAAAA,CAAQ,IAAIH,CAAAA,CAAQ,EAAE,CAAA,CACtBI,CAAAA,CAAU,GAAA,CAAIJ,CAAAA,CAAQ,IAAI,CAAA,CAC1BK,CAAAA,CAAe,KAAKL,CAAO,CAAA,CAAA,CAG7B,OAAOK,CACT,CAEA,SAASd,CAAAA,CAAoBS,CAAAA,CAAiD,CAC5E,IAAMM,CAAAA,CAASN,CAAAA,CAAQ,QAAU,EAAC,CAClC,OACEM,CAAAA,CAAO,IAAA,CAAMC,CAAAA,EAAUA,CAAAA,CAAM,SAAS,CAAA,EAAG,MAAQD,CAAAA,CAAO,CAAC,GAAG,IAAA,EAAQ,IAExE,CAEA,SAAST,CAAAA,CAAeG,CAAAA,CAA0C,CAChE,IAAMQ,CAAAA,CAAY,KAAK,KAAA,CAAMR,CAAAA,CAAQ,aAAeA,CAAAA,CAAQ,SAAA,EAAa,EAAE,CAAA,CAC3E,OAAO,MAAA,CAAO,QAAA,CAASQ,CAAS,CAAA,CAAIA,EAAY,CAClD,CAEA,SAASV,CAAAA,CAAgBE,CAAAA,CAA0C,CACjE,OAAOA,CAAAA,CAAQ,KAAA,EAASA,CAAAA,CAAQ,IAClC","file":"related-articles.mjs","sourcesContent":["import type { PublishedArticle, PublishedTopic } from \"../types/contracts\";\n\nexport interface RelatedArticleCandidate {\n readonly id: string;\n readonly slug: string;\n readonly title?: string;\n readonly publishedAt: string;\n readonly updatedAt?: string;\n readonly topics?: readonly PublishedTopic[];\n}\n\nexport interface RelatedFeaturedArticlesOptions {\n readonly limit?: number;\n}\n\nconst DEFAULT_RELATED_ARTICLE_LIMIT = 3;\n\nexport function getRelatedFeaturedArticles<\n TArticle extends RelatedArticleCandidate,\n>(\n currentArticle: RelatedArticleCandidate,\n articles: readonly TArticle[],\n options: RelatedFeaturedArticlesOptions = {},\n): TArticle[] {\n const limit = options.limit ?? DEFAULT_RELATED_ARTICLE_LIMIT;\n if (limit <= 0) return [];\n\n const currentPrimaryTopicSlug = getPrimaryTopicSlug(currentArticle);\n const eligibleArticles = getUniqueEligibleArticles(currentArticle, articles);\n const byNewest = (a: TArticle, b: TArticle) =>\n getArticleTime(b) - getArticleTime(a) || getArticleTitle(a).localeCompare(getArticleTitle(b));\n\n const samePrimaryTopic = currentPrimaryTopicSlug\n ? eligibleArticles\n .filter(\n (article) => getPrimaryTopicSlug(article) === currentPrimaryTopicSlug,\n )\n .sort(byNewest)\n : [];\n\n const sameTopicIds = new Set(samePrimaryTopic.map((article) => article.id));\n const fallbackArticles = eligibleArticles\n .filter((article) => !sameTopicIds.has(article.id))\n .sort(byNewest);\n\n return [...samePrimaryTopic, ...fallbackArticles].slice(0, limit);\n}\n\nfunction getUniqueEligibleArticles<TArticle extends RelatedArticleCandidate>(\n currentArticle: RelatedArticleCandidate,\n articles: readonly TArticle[],\n): TArticle[] {\n const seenIds = new Set<string>();\n const seenSlugs = new Set<string>();\n const uniqueArticles: TArticle[] = [];\n\n for (const article of articles) {\n if (article.id === currentArticle.id || article.slug === currentArticle.slug) {\n continue;\n }\n\n if (seenIds.has(article.id) || seenSlugs.has(article.slug)) {\n continue;\n }\n\n seenIds.add(article.id);\n seenSlugs.add(article.slug);\n uniqueArticles.push(article);\n }\n\n return uniqueArticles;\n}\n\nfunction getPrimaryTopicSlug(article: RelatedArticleCandidate): string | null {\n const topics = article.topics ?? [];\n return (\n topics.find((topic) => topic.isPrimary)?.slug ?? topics[0]?.slug ?? null\n );\n}\n\nfunction getArticleTime(article: RelatedArticleCandidate): number {\n const timestamp = Date.parse(article.publishedAt ?? article.updatedAt ?? \"\");\n return Number.isFinite(timestamp) ? timestamp : 0;\n}\n\nfunction getArticleTitle(article: RelatedArticleCandidate): string {\n return article.title ?? article.slug;\n}\n\nexport type RelatedPublishedArticle = PublishedArticle;\n"]}
@@ -0,0 +1,90 @@
1
+ import type { PublishedArticle, PublishedTopic } from "../types/contracts";
2
+
3
+ export interface RelatedArticleCandidate {
4
+ readonly id: string;
5
+ readonly slug: string;
6
+ readonly title?: string;
7
+ readonly publishedAt: string;
8
+ readonly updatedAt?: string;
9
+ readonly topics?: readonly PublishedTopic[];
10
+ }
11
+
12
+ export interface RelatedFeaturedArticlesOptions {
13
+ readonly limit?: number;
14
+ }
15
+
16
+ const DEFAULT_RELATED_ARTICLE_LIMIT = 3;
17
+
18
+ export function getRelatedFeaturedArticles<
19
+ TArticle extends RelatedArticleCandidate,
20
+ >(
21
+ currentArticle: RelatedArticleCandidate,
22
+ articles: readonly TArticle[],
23
+ options: RelatedFeaturedArticlesOptions = {},
24
+ ): TArticle[] {
25
+ const limit = options.limit ?? DEFAULT_RELATED_ARTICLE_LIMIT;
26
+ if (limit <= 0) return [];
27
+
28
+ const currentPrimaryTopicSlug = getPrimaryTopicSlug(currentArticle);
29
+ const eligibleArticles = getUniqueEligibleArticles(currentArticle, articles);
30
+ const byNewest = (a: TArticle, b: TArticle) =>
31
+ getArticleTime(b) - getArticleTime(a) || getArticleTitle(a).localeCompare(getArticleTitle(b));
32
+
33
+ const samePrimaryTopic = currentPrimaryTopicSlug
34
+ ? eligibleArticles
35
+ .filter(
36
+ (article) => getPrimaryTopicSlug(article) === currentPrimaryTopicSlug,
37
+ )
38
+ .sort(byNewest)
39
+ : [];
40
+
41
+ const sameTopicIds = new Set(samePrimaryTopic.map((article) => article.id));
42
+ const fallbackArticles = eligibleArticles
43
+ .filter((article) => !sameTopicIds.has(article.id))
44
+ .sort(byNewest);
45
+
46
+ return [...samePrimaryTopic, ...fallbackArticles].slice(0, limit);
47
+ }
48
+
49
+ function getUniqueEligibleArticles<TArticle extends RelatedArticleCandidate>(
50
+ currentArticle: RelatedArticleCandidate,
51
+ articles: readonly TArticle[],
52
+ ): TArticle[] {
53
+ const seenIds = new Set<string>();
54
+ const seenSlugs = new Set<string>();
55
+ const uniqueArticles: TArticle[] = [];
56
+
57
+ for (const article of articles) {
58
+ if (article.id === currentArticle.id || article.slug === currentArticle.slug) {
59
+ continue;
60
+ }
61
+
62
+ if (seenIds.has(article.id) || seenSlugs.has(article.slug)) {
63
+ continue;
64
+ }
65
+
66
+ seenIds.add(article.id);
67
+ seenSlugs.add(article.slug);
68
+ uniqueArticles.push(article);
69
+ }
70
+
71
+ return uniqueArticles;
72
+ }
73
+
74
+ function getPrimaryTopicSlug(article: RelatedArticleCandidate): string | null {
75
+ const topics = article.topics ?? [];
76
+ return (
77
+ topics.find((topic) => topic.isPrimary)?.slug ?? topics[0]?.slug ?? null
78
+ );
79
+ }
80
+
81
+ function getArticleTime(article: RelatedArticleCandidate): number {
82
+ const timestamp = Date.parse(article.publishedAt ?? article.updatedAt ?? "");
83
+ return Number.isFinite(timestamp) ? timestamp : 0;
84
+ }
85
+
86
+ function getArticleTitle(article: RelatedArticleCandidate): string {
87
+ return article.title ?? article.slug;
88
+ }
89
+
90
+ export type RelatedPublishedArticle = PublishedArticle;
@@ -1,4 +1,4 @@
1
- import { PublishedArticle, PublishedSite, PublishedAuthor } from './types/index.js';
1
+ import { a as PublishedArticle, b as PublishedSite, c as PublishedAuthor } from './index-Dr8HaKGm.js';
2
2
 
3
3
  /**
4
4
  * SEO utilities for canonical URL generation and validation.
@@ -1,4 +1,4 @@
1
- import { PublishedArticle, PublishedSite, PublishedAuthor } from './types/index.mjs';
1
+ import { a as PublishedArticle, b as PublishedSite, c as PublishedAuthor } from './index-Dr8HaKGm.mjs';
2
2
 
3
3
  /**
4
4
  * SEO utilities for canonical URL generation and validation.
@@ -1,2 +1,2 @@
1
- import './types/index.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-Dz9DB7py.mjs';
1
+ import './index-Dr8HaKGm.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-Dbl4CUGI.mjs';
package/dist/sitemap.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import './types/index.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-BPTdXb9C.js';
1
+ import './index-Dr8HaKGm.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-BmCh8BpN.js';
@@ -115,6 +115,34 @@ export interface PublishedNewsletterSettings {
115
115
  readonly publicSubscribeToken: string;
116
116
  }
117
117
 
118
+ /**
119
+ * Public post-article CTA configuration.
120
+ */
121
+ export interface PublishedArticleCta {
122
+ /** CTA heading */
123
+ readonly title: string;
124
+
125
+ /** Optional supporting copy */
126
+ readonly description?: string;
127
+
128
+ /** Primary sign-up action */
129
+ readonly primary?: PublishedArticleCtaAction;
130
+
131
+ /** Secondary learn-more action */
132
+ readonly secondary?: PublishedArticleCtaAction;
133
+ }
134
+
135
+ /**
136
+ * Public post-article CTA link.
137
+ */
138
+ export interface PublishedArticleCtaAction {
139
+ /** Button label */
140
+ readonly label: string;
141
+
142
+ /** Safe http(s) URL or same-site relative path */
143
+ readonly href: string;
144
+ }
145
+
118
146
  /**
119
147
  * Public topic/category taxonomy assigned to published articles.
120
148
  */
@@ -173,6 +201,9 @@ export interface PublishedArticle {
173
201
  /** Site information */
174
202
  readonly site: PublishedSite;
175
203
 
204
+ /** Optional post-article call-to-action */
205
+ readonly cta?: PublishedArticleCta;
206
+
176
207
  /** Public topic taxonomy assigned to the article */
177
208
  readonly topics?: readonly PublishedTopic[];
178
209
 
@@ -1,205 +1 @@
1
- /**
2
- * Public API contracts for the @xexr/blog-platform package.
3
- *
4
- * These types define the public interface between Bloggy and external blog platforms.
5
- * They must remain stable and backward-compatible to ensure smooth upgrades.
6
- *
7
- * IMPORTANT: These contracts should never import from the main Bloggy application
8
- * to maintain clean separation of concerns and prevent coupling.
9
- */
10
- /**
11
- * Public profile and social links for an author.
12
- */
13
- interface PublishedAuthorSocialLinks {
14
- /** Author website or profile URL */
15
- readonly website?: string;
16
- /** X/Twitter profile URL */
17
- readonly x?: string;
18
- /** Legacy Twitter profile URL */
19
- readonly twitter?: string;
20
- /** LinkedIn profile URL */
21
- readonly linkedin?: string;
22
- /** GitHub profile URL */
23
- readonly github?: string;
24
- /** Public contact email address */
25
- readonly email?: string;
26
- }
27
- /**
28
- * Represents a published author with essential information for bylines and attribution.
29
- */
30
- interface PublishedAuthor {
31
- /** Unique identifier for the author */
32
- readonly id: string;
33
- /** Author's full name */
34
- readonly name: string;
35
- /** URL-friendly slug for author pages */
36
- readonly slug: string;
37
- /** Author's bio/description (optional) */
38
- readonly bio?: string;
39
- /** Author avatar image URL (optional) */
40
- readonly avatarUrl?: string;
41
- /** Public profile and social links (optional) */
42
- readonly socialLinks?: PublishedAuthorSocialLinks;
43
- /** Pronouns for proper attribution */
44
- readonly pronouns?: string;
45
- /** Whether this is a guest author */
46
- readonly isGuest: boolean;
47
- /** Tone adjectives that describe this author's writing style */
48
- readonly toneAdjectives: readonly string[];
49
- }
50
- /**
51
- * Represents a published site with essential metadata for blog rendering.
52
- */
53
- interface PublishedSite {
54
- /** Unique identifier for the site */
55
- readonly id: string;
56
- /** Site name/title */
57
- readonly name: string;
58
- /** Site tagline/subtitle */
59
- readonly tagline: string;
60
- /** Site description */
61
- readonly description: string;
62
- /** Primary domain for the site */
63
- readonly domain: string;
64
- /** Site-wide tone adjectives for content context */
65
- readonly toneAdjectives: readonly string[];
66
- /** Default locale/language code */
67
- readonly locale: string;
68
- /** Optional Bloggy-owned newsletter capture configuration */
69
- readonly newsletter?: PublishedNewsletterSettings;
70
- }
71
- /**
72
- * Public configuration for Bloggy-owned newsletter capture.
73
- */
74
- interface PublishedNewsletterSettings {
75
- /** Whether the capture form should render */
76
- readonly enabled: boolean;
77
- /** Form heading */
78
- readonly title: string;
79
- /** Form supporting copy */
80
- readonly description: string;
81
- /** Submit button label */
82
- readonly buttonLabel: string;
83
- /** Base URL for the Bloggy instance that receives subscriptions */
84
- readonly captureBaseUrl: string;
85
- /** Site-scoped public token used to validate public submissions */
86
- readonly publicSubscribeToken: string;
87
- }
88
- /**
89
- * Public topic/category taxonomy assigned to published articles.
90
- */
91
- interface PublishedTopic {
92
- /** Unique identifier for the topic */
93
- readonly id: string;
94
- /** Display name */
95
- readonly name: string;
96
- /** URL-friendly slug for topic archive routing */
97
- readonly slug: string;
98
- /** Optional public description */
99
- readonly description?: string;
100
- /** Whether this is the article's primary topic */
101
- readonly isPrimary: boolean;
102
- }
103
- /**
104
- * Represents a published article with all necessary metadata for rendering
105
- * on external blog platforms.
106
- */
107
- interface PublishedArticle {
108
- /** Unique identifier for the article */
109
- readonly id: string;
110
- /** URL-friendly slug for routing */
111
- readonly slug: string;
112
- /** Article title/headline */
113
- readonly title: string;
114
- /** Brief excerpt/summary for meta descriptions and previews */
115
- readonly excerpt: string;
116
- /** Full article content in markdown format */
117
- readonly content: string;
118
- /** Estimated reading time in seconds */
119
- readonly readingSeconds: number;
120
- /** Word count for analytics */
121
- readonly wordCount: number;
122
- /** Publication date as ISO 8601 string */
123
- readonly publishedAt: string;
124
- /** Last updated date as ISO 8601 string */
125
- readonly updatedAt: string;
126
- /** Author information (can be null for anonymous posts) */
127
- readonly author: PublishedAuthor | null;
128
- /** Site information */
129
- readonly site: PublishedSite;
130
- /** Public topic taxonomy assigned to the article */
131
- readonly topics?: readonly PublishedTopic[];
132
- /** Featured image URL (optional) */
133
- readonly featuredImage?: string;
134
- /** Alt text for featured image */
135
- readonly featuredImageAlt?: string;
136
- /** Caption displayed with the featured image */
137
- readonly featuredImageCaption?: string;
138
- /** Credit text displayed with the featured image */
139
- readonly featuredImageCreditText?: string;
140
- /** Optional URL for the featured image credit */
141
- readonly featuredImageCreditUrl?: string;
142
- /** Featured image R2 key for CDN optimization (optional) */
143
- readonly featuredImageKey?: string;
144
- /** SEO meta tags */
145
- readonly meta: {
146
- /** Meta title (defaults to article title if not provided) */
147
- readonly title?: string;
148
- /** Meta description (defaults to excerpt if not provided) */
149
- readonly description?: string;
150
- /** Canonical URL for SEO */
151
- readonly canonicalUrl?: string;
152
- /** Keywords for SEO */
153
- readonly keywords: readonly string[];
154
- /** Open Graph image URL */
155
- readonly ogImage?: string;
156
- };
157
- /** Article version for optimistic locking */
158
- readonly version: number;
159
- }
160
- /**
161
- * Payload structure for publishing operations.
162
- * Used when sending publish/unpublish requests to blog platforms.
163
- */
164
- interface PublishPayload {
165
- /** The article to publish */
166
- readonly article: PublishedArticle;
167
- /** Idempotency key to prevent duplicate operations */
168
- readonly idempotencyKey: string;
169
- /** Optional slug override (defaults to article.slug) */
170
- readonly slug?: string;
171
- /** Optional meta description override (defaults to article.excerpt) */
172
- readonly metaDescription?: string;
173
- /** Platform-specific configuration */
174
- readonly platformConfig?: Record<string, unknown>;
175
- /** Whether this is a dry run (validation only) */
176
- readonly dryRun?: boolean;
177
- }
178
- /**
179
- * Result structure returned from publishing operations.
180
- */
181
- interface PublishResult {
182
- /** Whether the operation was successful */
183
- readonly success: boolean;
184
- /** The published article URL if successful */
185
- readonly url?: string;
186
- /** Platform-specific post ID */
187
- readonly platformPostId?: string;
188
- /** Publication timestamp as ISO 8601 string */
189
- readonly publishedAt?: string;
190
- /** Error information if unsuccessful */
191
- readonly error?: {
192
- /** Error code (PUB001-PUB011) */
193
- readonly code: string;
194
- /** Human-readable error message */
195
- readonly message: string;
196
- /** Additional error details */
197
- readonly details?: Record<string, unknown>;
198
- };
199
- /** Validation warnings (for dry runs or minor issues) */
200
- readonly warnings?: readonly string[];
201
- /** Idempotency key used for this operation */
202
- readonly idempotencyKey: string;
203
- }
204
-
205
- export type { PublishPayload, PublishResult, PublishedArticle, PublishedAuthor, PublishedAuthorSocialLinks, PublishedNewsletterSettings, PublishedSite, PublishedTopic };
1
+ export { g as PublishPayload, f as PublishResult, a as PublishedArticle, c as PublishedAuthor, h as PublishedAuthorSocialLinks, d as PublishedNewsletterSettings, b as PublishedSite, P as PublishedTopic } from '../index-Dr8HaKGm.mjs';
@@ -1,205 +1 @@
1
- /**
2
- * Public API contracts for the @xexr/blog-platform package.
3
- *
4
- * These types define the public interface between Bloggy and external blog platforms.
5
- * They must remain stable and backward-compatible to ensure smooth upgrades.
6
- *
7
- * IMPORTANT: These contracts should never import from the main Bloggy application
8
- * to maintain clean separation of concerns and prevent coupling.
9
- */
10
- /**
11
- * Public profile and social links for an author.
12
- */
13
- interface PublishedAuthorSocialLinks {
14
- /** Author website or profile URL */
15
- readonly website?: string;
16
- /** X/Twitter profile URL */
17
- readonly x?: string;
18
- /** Legacy Twitter profile URL */
19
- readonly twitter?: string;
20
- /** LinkedIn profile URL */
21
- readonly linkedin?: string;
22
- /** GitHub profile URL */
23
- readonly github?: string;
24
- /** Public contact email address */
25
- readonly email?: string;
26
- }
27
- /**
28
- * Represents a published author with essential information for bylines and attribution.
29
- */
30
- interface PublishedAuthor {
31
- /** Unique identifier for the author */
32
- readonly id: string;
33
- /** Author's full name */
34
- readonly name: string;
35
- /** URL-friendly slug for author pages */
36
- readonly slug: string;
37
- /** Author's bio/description (optional) */
38
- readonly bio?: string;
39
- /** Author avatar image URL (optional) */
40
- readonly avatarUrl?: string;
41
- /** Public profile and social links (optional) */
42
- readonly socialLinks?: PublishedAuthorSocialLinks;
43
- /** Pronouns for proper attribution */
44
- readonly pronouns?: string;
45
- /** Whether this is a guest author */
46
- readonly isGuest: boolean;
47
- /** Tone adjectives that describe this author's writing style */
48
- readonly toneAdjectives: readonly string[];
49
- }
50
- /**
51
- * Represents a published site with essential metadata for blog rendering.
52
- */
53
- interface PublishedSite {
54
- /** Unique identifier for the site */
55
- readonly id: string;
56
- /** Site name/title */
57
- readonly name: string;
58
- /** Site tagline/subtitle */
59
- readonly tagline: string;
60
- /** Site description */
61
- readonly description: string;
62
- /** Primary domain for the site */
63
- readonly domain: string;
64
- /** Site-wide tone adjectives for content context */
65
- readonly toneAdjectives: readonly string[];
66
- /** Default locale/language code */
67
- readonly locale: string;
68
- /** Optional Bloggy-owned newsletter capture configuration */
69
- readonly newsletter?: PublishedNewsletterSettings;
70
- }
71
- /**
72
- * Public configuration for Bloggy-owned newsletter capture.
73
- */
74
- interface PublishedNewsletterSettings {
75
- /** Whether the capture form should render */
76
- readonly enabled: boolean;
77
- /** Form heading */
78
- readonly title: string;
79
- /** Form supporting copy */
80
- readonly description: string;
81
- /** Submit button label */
82
- readonly buttonLabel: string;
83
- /** Base URL for the Bloggy instance that receives subscriptions */
84
- readonly captureBaseUrl: string;
85
- /** Site-scoped public token used to validate public submissions */
86
- readonly publicSubscribeToken: string;
87
- }
88
- /**
89
- * Public topic/category taxonomy assigned to published articles.
90
- */
91
- interface PublishedTopic {
92
- /** Unique identifier for the topic */
93
- readonly id: string;
94
- /** Display name */
95
- readonly name: string;
96
- /** URL-friendly slug for topic archive routing */
97
- readonly slug: string;
98
- /** Optional public description */
99
- readonly description?: string;
100
- /** Whether this is the article's primary topic */
101
- readonly isPrimary: boolean;
102
- }
103
- /**
104
- * Represents a published article with all necessary metadata for rendering
105
- * on external blog platforms.
106
- */
107
- interface PublishedArticle {
108
- /** Unique identifier for the article */
109
- readonly id: string;
110
- /** URL-friendly slug for routing */
111
- readonly slug: string;
112
- /** Article title/headline */
113
- readonly title: string;
114
- /** Brief excerpt/summary for meta descriptions and previews */
115
- readonly excerpt: string;
116
- /** Full article content in markdown format */
117
- readonly content: string;
118
- /** Estimated reading time in seconds */
119
- readonly readingSeconds: number;
120
- /** Word count for analytics */
121
- readonly wordCount: number;
122
- /** Publication date as ISO 8601 string */
123
- readonly publishedAt: string;
124
- /** Last updated date as ISO 8601 string */
125
- readonly updatedAt: string;
126
- /** Author information (can be null for anonymous posts) */
127
- readonly author: PublishedAuthor | null;
128
- /** Site information */
129
- readonly site: PublishedSite;
130
- /** Public topic taxonomy assigned to the article */
131
- readonly topics?: readonly PublishedTopic[];
132
- /** Featured image URL (optional) */
133
- readonly featuredImage?: string;
134
- /** Alt text for featured image */
135
- readonly featuredImageAlt?: string;
136
- /** Caption displayed with the featured image */
137
- readonly featuredImageCaption?: string;
138
- /** Credit text displayed with the featured image */
139
- readonly featuredImageCreditText?: string;
140
- /** Optional URL for the featured image credit */
141
- readonly featuredImageCreditUrl?: string;
142
- /** Featured image R2 key for CDN optimization (optional) */
143
- readonly featuredImageKey?: string;
144
- /** SEO meta tags */
145
- readonly meta: {
146
- /** Meta title (defaults to article title if not provided) */
147
- readonly title?: string;
148
- /** Meta description (defaults to excerpt if not provided) */
149
- readonly description?: string;
150
- /** Canonical URL for SEO */
151
- readonly canonicalUrl?: string;
152
- /** Keywords for SEO */
153
- readonly keywords: readonly string[];
154
- /** Open Graph image URL */
155
- readonly ogImage?: string;
156
- };
157
- /** Article version for optimistic locking */
158
- readonly version: number;
159
- }
160
- /**
161
- * Payload structure for publishing operations.
162
- * Used when sending publish/unpublish requests to blog platforms.
163
- */
164
- interface PublishPayload {
165
- /** The article to publish */
166
- readonly article: PublishedArticle;
167
- /** Idempotency key to prevent duplicate operations */
168
- readonly idempotencyKey: string;
169
- /** Optional slug override (defaults to article.slug) */
170
- readonly slug?: string;
171
- /** Optional meta description override (defaults to article.excerpt) */
172
- readonly metaDescription?: string;
173
- /** Platform-specific configuration */
174
- readonly platformConfig?: Record<string, unknown>;
175
- /** Whether this is a dry run (validation only) */
176
- readonly dryRun?: boolean;
177
- }
178
- /**
179
- * Result structure returned from publishing operations.
180
- */
181
- interface PublishResult {
182
- /** Whether the operation was successful */
183
- readonly success: boolean;
184
- /** The published article URL if successful */
185
- readonly url?: string;
186
- /** Platform-specific post ID */
187
- readonly platformPostId?: string;
188
- /** Publication timestamp as ISO 8601 string */
189
- readonly publishedAt?: string;
190
- /** Error information if unsuccessful */
191
- readonly error?: {
192
- /** Error code (PUB001-PUB011) */
193
- readonly code: string;
194
- /** Human-readable error message */
195
- readonly message: string;
196
- /** Additional error details */
197
- readonly details?: Record<string, unknown>;
198
- };
199
- /** Validation warnings (for dry runs or minor issues) */
200
- readonly warnings?: readonly string[];
201
- /** Idempotency key used for this operation */
202
- readonly idempotencyKey: string;
203
- }
204
-
205
- export type { PublishPayload, PublishResult, PublishedArticle, PublishedAuthor, PublishedAuthorSocialLinks, PublishedNewsletterSettings, PublishedSite, PublishedTopic };
1
+ export { g as PublishPayload, f as PublishResult, a as PublishedArticle, c as PublishedAuthor, h as PublishedAuthorSocialLinks, d as PublishedNewsletterSettings, b as PublishedSite, P as PublishedTopic } from '../index-Dr8HaKGm.js';