@fullstackdatasolutions/articles 0.8.2 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +237 -0
  2. package/README.md +209 -78
  3. package/dist/index.cjs +635 -274
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.cts +164 -56
  6. package/dist/index.d.ts +164 -56
  7. package/dist/index.js +614 -250
  8. package/dist/index.js.map +1 -1
  9. package/dist/nextjs.cjs +113 -38
  10. package/dist/nextjs.cjs.map +1 -1
  11. package/dist/nextjs.d.cts +71 -0
  12. package/dist/nextjs.d.ts +71 -0
  13. package/dist/nextjs.js +113 -38
  14. package/dist/nextjs.js.map +1 -1
  15. package/dist/server.cjs +394 -52
  16. package/dist/server.cjs.map +1 -1
  17. package/dist/server.d.cts +96 -6
  18. package/dist/server.d.ts +96 -6
  19. package/dist/server.js +382 -52
  20. package/dist/server.js.map +1 -1
  21. package/package.json +8 -5
  22. package/src/ArticleContent.tsx +8 -3
  23. package/src/ArticleDetailHero.tsx +27 -2
  24. package/src/ArticleSchemas.tsx +27 -27
  25. package/src/AuthorArticlesPage.tsx +60 -0
  26. package/src/AuthorCard.tsx +112 -0
  27. package/src/AuthorDetailHero.tsx +56 -0
  28. package/src/Breadcrumb.tsx +78 -0
  29. package/src/CategoryArticlesPage.tsx +62 -11
  30. package/src/__tests__/ArticleContent.test.tsx +18 -2
  31. package/src/__tests__/ArticleDetailHero.test.tsx +21 -1
  32. package/src/__tests__/ArticleSchemas.test.tsx +47 -2
  33. package/src/__tests__/AuthorArticlesPage.test.tsx +74 -0
  34. package/src/__tests__/AuthorCard.test.tsx +98 -0
  35. package/src/__tests__/AuthorDetailHero.test.tsx +51 -0
  36. package/src/__tests__/CategoryArticlesPage.test.tsx +31 -5
  37. package/src/__tests__/authorUtils.test.ts +89 -0
  38. package/src/__tests__/markdown.test.ts +79 -3
  39. package/src/__tests__/renderMdx.test.tsx +57 -0
  40. package/src/__tests__/seoUtils-authors.test.ts +160 -0
  41. package/src/__tests__/seoUtils.test.ts +106 -0
  42. package/src/__tests__/server-articles.test.ts +174 -3
  43. package/src/articleTypes.ts +33 -0
  44. package/src/articlesConfig.ts +67 -0
  45. package/src/authorUtils.ts +95 -0
  46. package/src/index.ts +32 -9
  47. package/src/markdown.ts +67 -8
  48. package/src/renderMdx.tsx +6 -3
  49. package/src/seoUtils.ts +279 -6
  50. package/src/server-articles.ts +124 -34
  51. package/src/server.ts +21 -2
package/dist/server.d.ts CHANGED
@@ -21,6 +21,7 @@ interface Article {
21
21
  date?: string;
22
22
  lastmod?: string;
23
23
  author: string;
24
+ authors?: string[];
24
25
  category: string;
25
26
  categories: string[];
26
27
  readTime: string;
@@ -45,6 +46,35 @@ interface CategoryInfo {
45
46
  count: number;
46
47
  featuredImage: string;
47
48
  }
49
+ interface AuthorSocial {
50
+ website?: string;
51
+ facebook?: string;
52
+ twitter?: string;
53
+ x?: string;
54
+ linkedin?: string;
55
+ instagram?: string;
56
+ youtube?: string;
57
+ tiktok?: string;
58
+ github?: string;
59
+ bluesky?: string;
60
+ threads?: string;
61
+ mastodon?: string;
62
+ medium?: string;
63
+ newsletter?: string;
64
+ other?: Record<string, string>;
65
+ }
66
+ interface AuthorProfile {
67
+ name: string;
68
+ slug: string;
69
+ bio: string;
70
+ avatar?: string;
71
+ url?: string;
72
+ social?: AuthorSocial;
73
+ }
74
+ interface BreadcrumbItem {
75
+ name: string;
76
+ url?: string;
77
+ }
48
78
 
49
79
  /** Keys for each renderable section of the articles listing page. */
50
80
  type ArticlesSection = 'hero' | 'search' | 'featured' | 'latest' | 'categories' | 'newsletter';
@@ -104,6 +134,41 @@ interface HeroConfig {
104
134
  /** Subheading paragraph displayed beneath the title. Default: the built-in description. */
105
135
  description?: string;
106
136
  }
137
+ /** Controls how article body links set target/rel attributes. */
138
+ type LinkTargetStrategy = 'external-new-tab' | 'all-new-tab' | 'same-tab';
139
+ type ArticleBreadcrumbToken = 'home' | 'articles' | 'primaryCategory' | 'folderPath' | 'articleTitle';
140
+ type CategoryBreadcrumbToken = 'home' | 'articles' | 'category';
141
+ type AuthorBreadcrumbToken = 'home' | 'articles' | 'authors' | 'authorName';
142
+ interface CustomBreadcrumbItem {
143
+ /** Label displayed in the breadcrumb trail. */
144
+ name: string;
145
+ /** Custom URL. Relative paths are resolved against `siteUrl` by server builders. */
146
+ url: string;
147
+ }
148
+ type ArticleBreadcrumbEntry = ArticleBreadcrumbToken | CustomBreadcrumbItem;
149
+ type CategoryBreadcrumbEntry = CategoryBreadcrumbToken | CustomBreadcrumbItem;
150
+ type AuthorBreadcrumbEntry = AuthorBreadcrumbToken | CustomBreadcrumbItem;
151
+ interface BreadcrumbLabels {
152
+ home?: string;
153
+ articles?: string;
154
+ authors?: string;
155
+ }
156
+ interface BreadcrumbsConfig {
157
+ /** Set to false to hide visible breadcrumbs and breadcrumb JSON-LD generated by the helper builders. */
158
+ show?: boolean;
159
+ /** Separator used by the visible Breadcrumb component. Default: '>'. */
160
+ separator?: string;
161
+ /** Set to false to render visible breadcrumbs without JSON-LD. Default: true. */
162
+ showSchema?: boolean;
163
+ /** Article breadcrumb trail. Example: ['primaryCategory', { name: 'Guides', url: '/guides' }, 'articleTitle']. */
164
+ article?: ArticleBreadcrumbEntry[];
165
+ /** Category breadcrumb trail. Default: ['home', 'articles', 'category']. */
166
+ category?: CategoryBreadcrumbEntry[];
167
+ /** Author breadcrumb trail. Default: ['home', 'articles', 'authors', 'authorName']. */
168
+ author?: AuthorBreadcrumbEntry[];
169
+ /** Optional label overrides for built-in breadcrumb items. */
170
+ labels?: BreadcrumbLabels;
171
+ }
107
172
  /** Top-level configuration object. Pass one instance to every library component. */
108
173
  interface ArticlesConfig {
109
174
  /** Canonical base URL of the site, used in metadata and JSON-LD. Example: `'https://yoursite.com'` */
@@ -139,12 +204,26 @@ interface ArticlesConfig {
139
204
  showBackToArticles?: boolean;
140
205
  /** Set to false to hide author names from UI and metadata. Default: true. */
141
206
  showAuthor?: boolean;
207
+ /** Author profiles keyed by slug. Omit to keep plain string author display. */
208
+ authors?: Record<string, AuthorProfile>;
209
+ /** Author slug used when article frontmatter omits author fields. */
210
+ defaultAuthor?: string;
211
+ /** Set to false to disable copied author page routes in consuming apps. Default: true. */
212
+ showAuthorPage?: boolean;
213
+ /** Set to false to disable breadcrumbs, or pass a config object to customize breadcrumb trails. */
214
+ breadcrumbs?: false | BreadcrumbsConfig;
215
+ /** Article body link target behavior. Default: `'external-new-tab'`. */
216
+ linkTargetStrategy?: LinkTargetStrategy;
142
217
  }
218
+ declare function getBreadcrumbsConfig(config: ArticlesConfig): BreadcrumbsConfig;
143
219
 
144
220
  declare function sanitizeImagePath(rawPath: string, articleSlug: string): string | null;
145
221
  declare function getAvailableArticleSlugs(): string[];
146
- declare const getArticleMetadata: (slug: string) => Promise<Article | null>;
147
- declare const getAllArticles: () => Promise<Article[]>;
222
+ declare function getAuthorBySlug(slug: string, config: ArticlesConfig): AuthorProfile | null;
223
+ declare function getArticleAuthors(article: Article, config: ArticlesConfig): AuthorProfile[];
224
+ declare function getAllAuthors(config: ArticlesConfig): AuthorProfile[];
225
+ declare const getArticleMetadata: (slug: string, config?: ArticlesConfig) => Promise<Article | null>;
226
+ declare const getAllArticles: (config?: ArticlesConfig) => Promise<Article[]>;
148
227
  declare function getAdjacentArticles(currentSlug: string): Promise<{
149
228
  previous: Article | null;
150
229
  next: Article | null;
@@ -157,20 +236,30 @@ declare function getAiRobotsTxtRules(): Promise<string>;
157
236
  declare function searchArticles(query: string, config?: ArticlesConfig): Promise<Article[]>;
158
237
  declare function categoryToSlug(category: string): string;
159
238
  declare function getAllCategories(): Promise<CategoryInfo[]>;
160
- declare function getArticlesByCategory(categorySlug: string): Promise<Article[]>;
239
+ declare function getArticlesByCategory(categorySlug: string, config?: ArticlesConfig): Promise<Article[]>;
240
+ declare function getArticlesByAuthor(authorSlug: string, config: ArticlesConfig): Promise<Article[]>;
161
241
 
242
+ declare function generateRssFeed(articles: Article[], config: ArticlesConfig): string;
162
243
  declare function generateArticleStaticParams(): {
163
244
  slug: string;
164
245
  }[];
165
246
  declare function generateCategoryStaticParams(): Promise<{
166
247
  category: string;
167
248
  }[]>;
249
+ declare function generateAuthorStaticParams(config: ArticlesConfig): {
250
+ author: string;
251
+ }[];
168
252
  declare function generateArticleMetadata(slug: string, config: ArticlesConfig): Promise<Metadata>;
169
253
  declare function generateArticlesIndexMetadata(config: ArticlesConfig): Metadata;
170
254
  declare function generateCategoryMetadata(categorySlug: string, config: ArticlesConfig): Promise<Metadata>;
255
+ declare function generateAuthorMetadata(authorSlug: string, config: ArticlesConfig): Promise<Metadata>;
256
+ declare function buildArticleBreadcrumbs(article: Pick<Article, 'slug' | 'title' | 'category'>, config: ArticlesConfig): BreadcrumbItem[];
257
+ declare function buildCategoryBreadcrumbs(category: string, config: ArticlesConfig, categoryName?: string): BreadcrumbItem[];
258
+ declare function buildAuthorBreadcrumbs(author: AuthorProfile, config: ArticlesConfig): BreadcrumbItem[];
259
+ declare function resolveAuthorAvatar(author: AuthorProfile, config: ArticlesConfig): string;
171
260
  declare function getArticleSitemapEntries(baseUrlOrConfig: string | ArticlesConfig): Promise<MetadataRoute.Sitemap>;
172
261
 
173
- declare function markdownToHtml(markdown: string, articleSlug?: string): Promise<string>;
262
+ declare function markdownToHtml(markdown: string, articleSlug?: string, config?: ArticlesConfig): Promise<string>;
174
263
  declare function extractToc(markdown: string): Promise<TocItem[]>;
175
264
 
176
265
  type ArticlesErrorCode = 'article-directory-read-failed' | 'article-load-failed' | 'article-markdown-load-failed' | 'markdown-conversion-failed' | 'unsafe-image-path';
@@ -187,8 +276,9 @@ declare function setArticlesErrorHandler(handler?: ArticlesErrorHandler): void;
187
276
  type ArticleContentProps = Readonly<{
188
277
  article: Article;
189
278
  className?: string;
279
+ config?: ArticlesConfig;
190
280
  }>;
191
- declare function ArticleContent({ article, className }: ArticleContentProps): Promise<react_jsx_runtime.JSX.Element>;
281
+ declare function ArticleContent({ article, className, config }: ArticleContentProps): Promise<react_jsx_runtime.JSX.Element>;
192
282
 
193
283
  type ArticleTOCProps = Readonly<{
194
284
  toc: TocItem[];
@@ -196,4 +286,4 @@ type ArticleTOCProps = Readonly<{
196
286
  }>;
197
287
  declare function ArticleTOC({ toc, className }: ArticleTOCProps): react_jsx_runtime.JSX.Element | null;
198
288
 
199
- export { type Article, ArticleContent, ArticleTOC, type ArticlesConfig, type ArticlesErrorCode, type ArticlesErrorContext, type ArticlesErrorHandler, type ArticlesErrorReport, type CategoryInfo, type TocItem, categoryToSlug, extractToc, generateArticleMetadata, generateArticleStaticParams, generateArticlesIndexMetadata, generateCategoryMetadata, generateCategoryStaticParams, getAdjacentArticles, getAiRobotsTxtRules, getAllArticles, getAllCategories, getArticleAiHeaders, getArticleMarkdown, getArticleMarkdownResponse, getArticleMarkdownUrl, getArticleMetadata, getArticleSitemapEntries, getArticlesByCategory, getAvailableArticleSlugs, markdownToHtml, sanitizeImagePath, searchArticles, setArticlesErrorHandler };
289
+ export { type Article, ArticleContent, ArticleTOC, type ArticlesConfig, type ArticlesErrorCode, type ArticlesErrorContext, type ArticlesErrorHandler, type ArticlesErrorReport, type AuthorProfile, type AuthorSocial, type BreadcrumbItem, type CategoryInfo, type LinkTargetStrategy, type TocItem, buildArticleBreadcrumbs, buildAuthorBreadcrumbs, buildCategoryBreadcrumbs, categoryToSlug, extractToc, generateArticleMetadata, generateArticleStaticParams, generateArticlesIndexMetadata, generateAuthorMetadata, generateAuthorStaticParams, generateCategoryMetadata, generateCategoryStaticParams, generateRssFeed, getAdjacentArticles, getAiRobotsTxtRules, getAllArticles, getAllAuthors, getAllCategories, getArticleAiHeaders, getArticleAuthors, getArticleMarkdown, getArticleMarkdownResponse, getArticleMarkdownUrl, getArticleMetadata, getArticleSitemapEntries, getArticlesByAuthor, getArticlesByCategory, getAuthorBySlug, getAvailableArticleSlugs, getBreadcrumbsConfig, markdownToHtml, resolveAuthorAvatar, sanitizeImagePath, searchArticles, setArticlesErrorHandler };