@zoyth/simple-site-framework 1.0.4 → 1.1.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.
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { ThemeConfig, HeroContent, LocalizedString, AboutContent, ServicesContent, HeaderConfig, FooterConfig } from './config/index.mjs';
1
+ import { ThemeConfig, LocalizedString, HeroContent, AboutContent, ServicesContent, HeaderConfig, FooterConfig } from './config/index.mjs';
2
2
  export { FooterSection, LogoConfig, NavDropdown, NavDropdownItem, NavItem, NavLink, NavigationConfig, ScriptsConfig, ServiceItem, SiteContent, SiteMetadata, WhyChooseUsContent } from './config/index.mjs';
3
3
  import { NextRequest, NextResponse } from 'next/server';
4
4
  import { ClassValue } from 'clsx';
@@ -128,6 +128,117 @@ declare function getAllPolicies(locale: string, contentDir?: string): Promise<Om
128
128
  */
129
129
  declare function getPolicyLocales(slug: string, contentDir?: string): string[];
130
130
 
131
+ interface BlogPostMetadata {
132
+ title: string;
133
+ excerpt: string;
134
+ author: string;
135
+ /** ISO date (YYYY-MM-DD) */
136
+ date: string;
137
+ /** Reading time in minutes */
138
+ readTime: number;
139
+ tags: string[];
140
+ featured?: boolean;
141
+ /** Featured image URL */
142
+ image?: string;
143
+ imageAlt?: string;
144
+ [key: string]: unknown;
145
+ }
146
+ interface BlogPost {
147
+ /** Compiled MDX content */
148
+ content: JSX.Element;
149
+ /** Frontmatter metadata */
150
+ metadata: BlogPostMetadata;
151
+ /** Blog post slug (filename without locale/extension) */
152
+ slug: string;
153
+ /** Locale */
154
+ locale: string;
155
+ }
156
+ /**
157
+ * Load a blog post markdown file and compile it to React
158
+ *
159
+ * @param slug - Blog post slug (e.g., 'getting-started')
160
+ * @param locale - Locale code (e.g., 'en', 'fr')
161
+ * @param contentDir - Directory containing blog files @default 'src/content/blog'
162
+ * @returns Compiled blog post with content and metadata
163
+ */
164
+ declare function loadBlogPost(slug: string, locale: string, contentDir?: string): Promise<BlogPost>;
165
+ /**
166
+ * Get all unique blog post slugs (without locale suffix)
167
+ *
168
+ * @param contentDir - Directory containing blog files @default 'src/content/blog'
169
+ * @returns Array of blog post slugs
170
+ */
171
+ declare function getBlogPostSlugs(contentDir?: string): string[];
172
+ /**
173
+ * Get all blog posts for a specific locale with their metadata, sorted by date descending
174
+ *
175
+ * @param locale - Locale code
176
+ * @param contentDir - Directory containing blog files @default 'src/content/blog'
177
+ * @returns Array of blog posts with metadata (without content)
178
+ */
179
+ declare function getAllBlogPosts(locale: string, contentDir?: string): Promise<Omit<BlogPost, 'content'>[]>;
180
+ /**
181
+ * Get available locales for a specific blog post
182
+ *
183
+ * @param slug - Blog post slug
184
+ * @param contentDir - Directory containing blog files @default 'src/content/blog'
185
+ * @returns Array of locale codes
186
+ */
187
+ declare function getBlogPostLocales(slug: string, contentDir?: string): string[];
188
+ /**
189
+ * Get all blog posts matching a specific tag, sorted by date descending
190
+ *
191
+ * @param tag - Tag to filter by
192
+ * @param locale - Locale code
193
+ * @param contentDir - Directory containing blog files @default 'src/content/blog'
194
+ */
195
+ declare function getBlogPostsByTag(tag: string, locale: string, contentDir?: string): Promise<Omit<BlogPost, 'content'>[]>;
196
+ /**
197
+ * Get featured blog posts, sorted by date descending
198
+ *
199
+ * @param locale - Locale code
200
+ * @param contentDir - Directory containing blog files @default 'src/content/blog'
201
+ */
202
+ declare function getFeaturedBlogPosts(locale: string, contentDir?: string): Promise<Omit<BlogPost, 'content'>[]>;
203
+ /**
204
+ * Get blog posts related to a given post by shared tags
205
+ *
206
+ * Sorted by number of shared tags (descending), then by date (descending).
207
+ *
208
+ * @param slug - Source blog post slug
209
+ * @param locale - Locale code
210
+ * @param count - Maximum number of related posts to return @default 3
211
+ * @param contentDir - Directory containing blog files @default 'src/content/blog'
212
+ */
213
+ declare function getRelatedBlogPosts(slug: string, locale: string, count?: number, contentDir?: string): Promise<Omit<BlogPost, 'content'>[]>;
214
+ interface TagCount {
215
+ tag: string;
216
+ count: number;
217
+ }
218
+ /**
219
+ * Get all unique tags across all blog posts with their occurrence count
220
+ *
221
+ * Sorted by count descending.
222
+ *
223
+ * @param locale - Locale code
224
+ * @param contentDir - Directory containing blog files @default 'src/content/blog'
225
+ */
226
+ declare function getAllTags(locale: string, contentDir?: string): Promise<TagCount[]>;
227
+
228
+ interface RssFeedOptions {
229
+ siteUrl: string;
230
+ siteName: string;
231
+ description: LocalizedString | string;
232
+ locale: string;
233
+ contentDir?: string;
234
+ }
235
+ /**
236
+ * Generate an RSS 2.0 feed from blog posts
237
+ *
238
+ * @returns XML string suitable for serving as application/rss+xml
239
+ */
240
+ declare function generateBlogRssFeed(options: RssFeedOptions): Promise<string>;
241
+
131
242
  /**
132
243
  * Helper to get localized string from navigation
133
244
  */
@@ -2181,4 +2292,4 @@ interface RadioProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'
2181
2292
  size?: 'sm' | 'md' | 'lg';
2182
2293
  }
2183
2294
 
2184
- export { type A11yAnnouncerProps, type ABTestEvent, AboutContent, type AboutSectionProps, type AggregateRating, type AnalyticsEvent, type AnnouncementPriority, type Answer, type Article, type BreadcrumbItem, type BreadcrumbList, type BreadcrumbProps, type ButtonProps, type CTAClickEvent, type CTAType, type CardProps, type ChangeFrequency, type CheckboxProps, type CodeBlockProps, type ContactFormConfig, type ContactFormData, type ContactPoint, type ContactSectionProps, type ConversionEvent, type ConversionType, type EventCategory, type FAQPage, type Feature, type FeatureAction, type FeatureCategory, type FeatureEvent, FeaturesGrid, FooterConfig, type FooterProps, type FormAction, type FormEvent, type FormFieldProps, HeaderConfig, type HeaderProps, type HeroAnimations, HeroContent, type HeroSectionProps, type I18nConfig, type IconName, type IconProps, type InputProps, LOCALE_COOKIE_NAME, type LanguagePreference, type LanguageSwitcherProps, type LazySectionProps, type ListItem, type Locale, type LocaleCookieConfig, type LocalePrefix, LocalizedString, type Location, type MetadataOptions, type ModalProps, type NavigationEvent, type NavigationLocation, type Offer, type Organization, type PageViewEvent, type Person, type Policy, type PolicyMetadata, type PostalAddress, type PricingAction, type PricingEvent, type Product, type Question, type RadioProps, type Rating, type Review, type ScrollDepth, type ScrollDepthEvent, type SearchAction, type SelectProps, type ServicePageLayoutProps, ServicesContent, type ServicesSectionProps, type SitemapConfig, type SitemapEntry, type SkipLinkProps, type SlugTranslations, type TabsProps, type TestimonialSectionProps, type TextareaProps, ThemeConfig, type Thing, TrackedLink, type ValidLocale, type VideoAction, type VideoEvent, type WebSite, cn, createArticle, createBreadcrumbList, createFAQPage, createI18nMiddleware, createMultiLanguageEntries, createOrganization, createProduct, createSitemapEntry, createWebSite, creditCardSchema, dateString, defaultLocale, defaultSlugTranslations, emailSchema, fileSchema, formErrorMessages, formatCurrency, formatDate, formatDateRange, formatFileSize, formatList, formatLocaleDisplay, formatNumber, formatRelativeTime, generateArticleMetadata, generateDesignTokens, generateMetadata, generateSitemap, generateThemeCSS, getAllPolicies, getAlternateLocales, getDefaultLocale, getErrorMessage, getFontConfig, getFontVariables, getI18nConfig, getLocaleAutonym, getLocaleCookieConfig, getLocaleFromCookie, getLocaleLabel, getLocaleLabels, getLocaleName, getLocaleNames, getLocalePrefix, getLocales, getLocalizedString, getNavigationString, getPolicyLocales, getPolicySlugs, getRelativeTime, getRtlLocales, getTextDirection, imageSchema, isI18nConfigInitialized, isLocaleDetectionEnabled, isRtlLocale, isSupportedLocale, loadPolicy, locales, matchLocale, mustBeTrue, normalizeLocale, numericString, optionalString, passwordSchema, phoneSchema, postalCodeSchema, replaceVariables, requiredString, serializeStructuredData, setI18nConfig, setLocaleCookie, trackABTestEvent, trackCTAClick, trackConversion, trackError, trackEvent, trackFeatureEngagement, trackFormEvent, trackNavigation, trackOutboundLink, trackPageView, trackPricingEvent, trackResourceDownload, trackScrollDepth, trackSearch, trackVideoEvent, translateSlug, urlSchema, validateLocale, validateSitemap, validateSitemapEntry };
2295
+ export { type A11yAnnouncerProps, type ABTestEvent, AboutContent, type AboutSectionProps, type AggregateRating, type AnalyticsEvent, type AnnouncementPriority, type Answer, type Article, type BlogPost, type BlogPostMetadata, type BreadcrumbItem, type BreadcrumbList, type BreadcrumbProps, type ButtonProps, type CTAClickEvent, type CTAType, type CardProps, type ChangeFrequency, type CheckboxProps, type CodeBlockProps, type ContactFormConfig, type ContactFormData, type ContactPoint, type ContactSectionProps, type ConversionEvent, type ConversionType, type EventCategory, type FAQPage, type Feature, type FeatureAction, type FeatureCategory, type FeatureEvent, FeaturesGrid, FooterConfig, type FooterProps, type FormAction, type FormEvent, type FormFieldProps, HeaderConfig, type HeaderProps, type HeroAnimations, HeroContent, type HeroSectionProps, type I18nConfig, type IconName, type IconProps, type InputProps, LOCALE_COOKIE_NAME, type LanguagePreference, type LanguageSwitcherProps, type LazySectionProps, type ListItem, type Locale, type LocaleCookieConfig, type LocalePrefix, LocalizedString, type Location, type MetadataOptions, type ModalProps, type NavigationEvent, type NavigationLocation, type Offer, type Organization, type PageViewEvent, type Person, type Policy, type PolicyMetadata, type PostalAddress, type PricingAction, type PricingEvent, type Product, type Question, type RadioProps, type Rating, type Review, type RssFeedOptions, type ScrollDepth, type ScrollDepthEvent, type SearchAction, type SelectProps, type ServicePageLayoutProps, ServicesContent, type ServicesSectionProps, type SitemapConfig, type SitemapEntry, type SkipLinkProps, type SlugTranslations, type TabsProps, type TagCount, type TestimonialSectionProps, type TextareaProps, ThemeConfig, type Thing, TrackedLink, type ValidLocale, type VideoAction, type VideoEvent, type WebSite, cn, createArticle, createBreadcrumbList, createFAQPage, createI18nMiddleware, createMultiLanguageEntries, createOrganization, createProduct, createSitemapEntry, createWebSite, creditCardSchema, dateString, defaultLocale, defaultSlugTranslations, emailSchema, fileSchema, formErrorMessages, formatCurrency, formatDate, formatDateRange, formatFileSize, formatList, formatLocaleDisplay, formatNumber, formatRelativeTime, generateArticleMetadata, generateBlogRssFeed, generateDesignTokens, generateMetadata, generateSitemap, generateThemeCSS, getAllBlogPosts, getAllPolicies, getAllTags, getAlternateLocales, getBlogPostLocales, getBlogPostSlugs, getBlogPostsByTag, getDefaultLocale, getErrorMessage, getFeaturedBlogPosts, getFontConfig, getFontVariables, getI18nConfig, getLocaleAutonym, getLocaleCookieConfig, getLocaleFromCookie, getLocaleLabel, getLocaleLabels, getLocaleName, getLocaleNames, getLocalePrefix, getLocales, getLocalizedString, getNavigationString, getPolicyLocales, getPolicySlugs, getRelatedBlogPosts, getRelativeTime, getRtlLocales, getTextDirection, imageSchema, isI18nConfigInitialized, isLocaleDetectionEnabled, isRtlLocale, isSupportedLocale, loadBlogPost, loadPolicy, locales, matchLocale, mustBeTrue, normalizeLocale, numericString, optionalString, passwordSchema, phoneSchema, postalCodeSchema, replaceVariables, requiredString, serializeStructuredData, setI18nConfig, setLocaleCookie, trackABTestEvent, trackCTAClick, trackConversion, trackError, trackEvent, trackFeatureEngagement, trackFormEvent, trackNavigation, trackOutboundLink, trackPageView, trackPricingEvent, trackResourceDownload, trackScrollDepth, trackSearch, trackVideoEvent, translateSlug, urlSchema, validateLocale, validateSitemap, validateSitemapEntry };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ThemeConfig, HeroContent, LocalizedString, AboutContent, ServicesContent, HeaderConfig, FooterConfig } from './config/index.js';
1
+ import { ThemeConfig, LocalizedString, HeroContent, AboutContent, ServicesContent, HeaderConfig, FooterConfig } from './config/index.js';
2
2
  export { FooterSection, LogoConfig, NavDropdown, NavDropdownItem, NavItem, NavLink, NavigationConfig, ScriptsConfig, ServiceItem, SiteContent, SiteMetadata, WhyChooseUsContent } from './config/index.js';
3
3
  import { NextRequest, NextResponse } from 'next/server';
4
4
  import { ClassValue } from 'clsx';
@@ -128,6 +128,117 @@ declare function getAllPolicies(locale: string, contentDir?: string): Promise<Om
128
128
  */
129
129
  declare function getPolicyLocales(slug: string, contentDir?: string): string[];
130
130
 
131
+ interface BlogPostMetadata {
132
+ title: string;
133
+ excerpt: string;
134
+ author: string;
135
+ /** ISO date (YYYY-MM-DD) */
136
+ date: string;
137
+ /** Reading time in minutes */
138
+ readTime: number;
139
+ tags: string[];
140
+ featured?: boolean;
141
+ /** Featured image URL */
142
+ image?: string;
143
+ imageAlt?: string;
144
+ [key: string]: unknown;
145
+ }
146
+ interface BlogPost {
147
+ /** Compiled MDX content */
148
+ content: JSX.Element;
149
+ /** Frontmatter metadata */
150
+ metadata: BlogPostMetadata;
151
+ /** Blog post slug (filename without locale/extension) */
152
+ slug: string;
153
+ /** Locale */
154
+ locale: string;
155
+ }
156
+ /**
157
+ * Load a blog post markdown file and compile it to React
158
+ *
159
+ * @param slug - Blog post slug (e.g., 'getting-started')
160
+ * @param locale - Locale code (e.g., 'en', 'fr')
161
+ * @param contentDir - Directory containing blog files @default 'src/content/blog'
162
+ * @returns Compiled blog post with content and metadata
163
+ */
164
+ declare function loadBlogPost(slug: string, locale: string, contentDir?: string): Promise<BlogPost>;
165
+ /**
166
+ * Get all unique blog post slugs (without locale suffix)
167
+ *
168
+ * @param contentDir - Directory containing blog files @default 'src/content/blog'
169
+ * @returns Array of blog post slugs
170
+ */
171
+ declare function getBlogPostSlugs(contentDir?: string): string[];
172
+ /**
173
+ * Get all blog posts for a specific locale with their metadata, sorted by date descending
174
+ *
175
+ * @param locale - Locale code
176
+ * @param contentDir - Directory containing blog files @default 'src/content/blog'
177
+ * @returns Array of blog posts with metadata (without content)
178
+ */
179
+ declare function getAllBlogPosts(locale: string, contentDir?: string): Promise<Omit<BlogPost, 'content'>[]>;
180
+ /**
181
+ * Get available locales for a specific blog post
182
+ *
183
+ * @param slug - Blog post slug
184
+ * @param contentDir - Directory containing blog files @default 'src/content/blog'
185
+ * @returns Array of locale codes
186
+ */
187
+ declare function getBlogPostLocales(slug: string, contentDir?: string): string[];
188
+ /**
189
+ * Get all blog posts matching a specific tag, sorted by date descending
190
+ *
191
+ * @param tag - Tag to filter by
192
+ * @param locale - Locale code
193
+ * @param contentDir - Directory containing blog files @default 'src/content/blog'
194
+ */
195
+ declare function getBlogPostsByTag(tag: string, locale: string, contentDir?: string): Promise<Omit<BlogPost, 'content'>[]>;
196
+ /**
197
+ * Get featured blog posts, sorted by date descending
198
+ *
199
+ * @param locale - Locale code
200
+ * @param contentDir - Directory containing blog files @default 'src/content/blog'
201
+ */
202
+ declare function getFeaturedBlogPosts(locale: string, contentDir?: string): Promise<Omit<BlogPost, 'content'>[]>;
203
+ /**
204
+ * Get blog posts related to a given post by shared tags
205
+ *
206
+ * Sorted by number of shared tags (descending), then by date (descending).
207
+ *
208
+ * @param slug - Source blog post slug
209
+ * @param locale - Locale code
210
+ * @param count - Maximum number of related posts to return @default 3
211
+ * @param contentDir - Directory containing blog files @default 'src/content/blog'
212
+ */
213
+ declare function getRelatedBlogPosts(slug: string, locale: string, count?: number, contentDir?: string): Promise<Omit<BlogPost, 'content'>[]>;
214
+ interface TagCount {
215
+ tag: string;
216
+ count: number;
217
+ }
218
+ /**
219
+ * Get all unique tags across all blog posts with their occurrence count
220
+ *
221
+ * Sorted by count descending.
222
+ *
223
+ * @param locale - Locale code
224
+ * @param contentDir - Directory containing blog files @default 'src/content/blog'
225
+ */
226
+ declare function getAllTags(locale: string, contentDir?: string): Promise<TagCount[]>;
227
+
228
+ interface RssFeedOptions {
229
+ siteUrl: string;
230
+ siteName: string;
231
+ description: LocalizedString | string;
232
+ locale: string;
233
+ contentDir?: string;
234
+ }
235
+ /**
236
+ * Generate an RSS 2.0 feed from blog posts
237
+ *
238
+ * @returns XML string suitable for serving as application/rss+xml
239
+ */
240
+ declare function generateBlogRssFeed(options: RssFeedOptions): Promise<string>;
241
+
131
242
  /**
132
243
  * Helper to get localized string from navigation
133
244
  */
@@ -2181,4 +2292,4 @@ interface RadioProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'
2181
2292
  size?: 'sm' | 'md' | 'lg';
2182
2293
  }
2183
2294
 
2184
- export { type A11yAnnouncerProps, type ABTestEvent, AboutContent, type AboutSectionProps, type AggregateRating, type AnalyticsEvent, type AnnouncementPriority, type Answer, type Article, type BreadcrumbItem, type BreadcrumbList, type BreadcrumbProps, type ButtonProps, type CTAClickEvent, type CTAType, type CardProps, type ChangeFrequency, type CheckboxProps, type CodeBlockProps, type ContactFormConfig, type ContactFormData, type ContactPoint, type ContactSectionProps, type ConversionEvent, type ConversionType, type EventCategory, type FAQPage, type Feature, type FeatureAction, type FeatureCategory, type FeatureEvent, FeaturesGrid, FooterConfig, type FooterProps, type FormAction, type FormEvent, type FormFieldProps, HeaderConfig, type HeaderProps, type HeroAnimations, HeroContent, type HeroSectionProps, type I18nConfig, type IconName, type IconProps, type InputProps, LOCALE_COOKIE_NAME, type LanguagePreference, type LanguageSwitcherProps, type LazySectionProps, type ListItem, type Locale, type LocaleCookieConfig, type LocalePrefix, LocalizedString, type Location, type MetadataOptions, type ModalProps, type NavigationEvent, type NavigationLocation, type Offer, type Organization, type PageViewEvent, type Person, type Policy, type PolicyMetadata, type PostalAddress, type PricingAction, type PricingEvent, type Product, type Question, type RadioProps, type Rating, type Review, type ScrollDepth, type ScrollDepthEvent, type SearchAction, type SelectProps, type ServicePageLayoutProps, ServicesContent, type ServicesSectionProps, type SitemapConfig, type SitemapEntry, type SkipLinkProps, type SlugTranslations, type TabsProps, type TestimonialSectionProps, type TextareaProps, ThemeConfig, type Thing, TrackedLink, type ValidLocale, type VideoAction, type VideoEvent, type WebSite, cn, createArticle, createBreadcrumbList, createFAQPage, createI18nMiddleware, createMultiLanguageEntries, createOrganization, createProduct, createSitemapEntry, createWebSite, creditCardSchema, dateString, defaultLocale, defaultSlugTranslations, emailSchema, fileSchema, formErrorMessages, formatCurrency, formatDate, formatDateRange, formatFileSize, formatList, formatLocaleDisplay, formatNumber, formatRelativeTime, generateArticleMetadata, generateDesignTokens, generateMetadata, generateSitemap, generateThemeCSS, getAllPolicies, getAlternateLocales, getDefaultLocale, getErrorMessage, getFontConfig, getFontVariables, getI18nConfig, getLocaleAutonym, getLocaleCookieConfig, getLocaleFromCookie, getLocaleLabel, getLocaleLabels, getLocaleName, getLocaleNames, getLocalePrefix, getLocales, getLocalizedString, getNavigationString, getPolicyLocales, getPolicySlugs, getRelativeTime, getRtlLocales, getTextDirection, imageSchema, isI18nConfigInitialized, isLocaleDetectionEnabled, isRtlLocale, isSupportedLocale, loadPolicy, locales, matchLocale, mustBeTrue, normalizeLocale, numericString, optionalString, passwordSchema, phoneSchema, postalCodeSchema, replaceVariables, requiredString, serializeStructuredData, setI18nConfig, setLocaleCookie, trackABTestEvent, trackCTAClick, trackConversion, trackError, trackEvent, trackFeatureEngagement, trackFormEvent, trackNavigation, trackOutboundLink, trackPageView, trackPricingEvent, trackResourceDownload, trackScrollDepth, trackSearch, trackVideoEvent, translateSlug, urlSchema, validateLocale, validateSitemap, validateSitemapEntry };
2295
+ export { type A11yAnnouncerProps, type ABTestEvent, AboutContent, type AboutSectionProps, type AggregateRating, type AnalyticsEvent, type AnnouncementPriority, type Answer, type Article, type BlogPost, type BlogPostMetadata, type BreadcrumbItem, type BreadcrumbList, type BreadcrumbProps, type ButtonProps, type CTAClickEvent, type CTAType, type CardProps, type ChangeFrequency, type CheckboxProps, type CodeBlockProps, type ContactFormConfig, type ContactFormData, type ContactPoint, type ContactSectionProps, type ConversionEvent, type ConversionType, type EventCategory, type FAQPage, type Feature, type FeatureAction, type FeatureCategory, type FeatureEvent, FeaturesGrid, FooterConfig, type FooterProps, type FormAction, type FormEvent, type FormFieldProps, HeaderConfig, type HeaderProps, type HeroAnimations, HeroContent, type HeroSectionProps, type I18nConfig, type IconName, type IconProps, type InputProps, LOCALE_COOKIE_NAME, type LanguagePreference, type LanguageSwitcherProps, type LazySectionProps, type ListItem, type Locale, type LocaleCookieConfig, type LocalePrefix, LocalizedString, type Location, type MetadataOptions, type ModalProps, type NavigationEvent, type NavigationLocation, type Offer, type Organization, type PageViewEvent, type Person, type Policy, type PolicyMetadata, type PostalAddress, type PricingAction, type PricingEvent, type Product, type Question, type RadioProps, type Rating, type Review, type RssFeedOptions, type ScrollDepth, type ScrollDepthEvent, type SearchAction, type SelectProps, type ServicePageLayoutProps, ServicesContent, type ServicesSectionProps, type SitemapConfig, type SitemapEntry, type SkipLinkProps, type SlugTranslations, type TabsProps, type TagCount, type TestimonialSectionProps, type TextareaProps, ThemeConfig, type Thing, TrackedLink, type ValidLocale, type VideoAction, type VideoEvent, type WebSite, cn, createArticle, createBreadcrumbList, createFAQPage, createI18nMiddleware, createMultiLanguageEntries, createOrganization, createProduct, createSitemapEntry, createWebSite, creditCardSchema, dateString, defaultLocale, defaultSlugTranslations, emailSchema, fileSchema, formErrorMessages, formatCurrency, formatDate, formatDateRange, formatFileSize, formatList, formatLocaleDisplay, formatNumber, formatRelativeTime, generateArticleMetadata, generateBlogRssFeed, generateDesignTokens, generateMetadata, generateSitemap, generateThemeCSS, getAllBlogPosts, getAllPolicies, getAllTags, getAlternateLocales, getBlogPostLocales, getBlogPostSlugs, getBlogPostsByTag, getDefaultLocale, getErrorMessage, getFeaturedBlogPosts, getFontConfig, getFontVariables, getI18nConfig, getLocaleAutonym, getLocaleCookieConfig, getLocaleFromCookie, getLocaleLabel, getLocaleLabels, getLocaleName, getLocaleNames, getLocalePrefix, getLocales, getLocalizedString, getNavigationString, getPolicyLocales, getPolicySlugs, getRelatedBlogPosts, getRelativeTime, getRtlLocales, getTextDirection, imageSchema, isI18nConfigInitialized, isLocaleDetectionEnabled, isRtlLocale, isSupportedLocale, loadBlogPost, loadPolicy, locales, matchLocale, mustBeTrue, normalizeLocale, numericString, optionalString, passwordSchema, phoneSchema, postalCodeSchema, replaceVariables, requiredString, serializeStructuredData, setI18nConfig, setLocaleCookie, trackABTestEvent, trackCTAClick, trackConversion, trackError, trackEvent, trackFeatureEngagement, trackFormEvent, trackNavigation, trackOutboundLink, trackPageView, trackPricingEvent, trackResourceDownload, trackScrollDepth, trackSearch, trackVideoEvent, translateSlug, urlSchema, validateLocale, validateSitemap, validateSitemapEntry };
package/dist/index.js CHANGED
@@ -204,14 +204,21 @@ __export(src_exports, {
204
204
  formatNumber: () => formatNumber,
205
205
  formatRelativeTime: () => formatRelativeTime,
206
206
  generateArticleMetadata: () => generateArticleMetadata,
207
+ generateBlogRssFeed: () => generateBlogRssFeed,
207
208
  generateDesignTokens: () => generateDesignTokens,
208
209
  generateMetadata: () => generateMetadata,
209
210
  generateSitemap: () => generateSitemap,
210
211
  generateThemeCSS: () => generateThemeCSS,
212
+ getAllBlogPosts: () => getAllBlogPosts,
211
213
  getAllPolicies: () => getAllPolicies,
214
+ getAllTags: () => getAllTags,
212
215
  getAlternateLocales: () => getAlternateLocales,
216
+ getBlogPostLocales: () => getBlogPostLocales,
217
+ getBlogPostSlugs: () => getBlogPostSlugs,
218
+ getBlogPostsByTag: () => getBlogPostsByTag,
213
219
  getDefaultLocale: () => getDefaultLocale,
214
220
  getErrorMessage: () => getErrorMessage,
221
+ getFeaturedBlogPosts: () => getFeaturedBlogPosts,
215
222
  getFontConfig: () => getFontConfig,
216
223
  getFontVariables: () => getFontVariables,
217
224
  getI18nConfig: () => getI18nConfig,
@@ -228,6 +235,7 @@ __export(src_exports, {
228
235
  getNavigationString: () => getNavigationString,
229
236
  getPolicyLocales: () => getPolicyLocales,
230
237
  getPolicySlugs: () => getPolicySlugs,
238
+ getRelatedBlogPosts: () => getRelatedBlogPosts,
231
239
  getRelativeTime: () => getRelativeTime,
232
240
  getRtlLocales: () => getRtlLocales,
233
241
  getTextDirection: () => getTextDirection,
@@ -236,6 +244,7 @@ __export(src_exports, {
236
244
  isLocaleDetectionEnabled: () => isLocaleDetectionEnabled,
237
245
  isRtlLocale: () => isRtlLocale,
238
246
  isSupportedLocale: () => isSupportedLocale,
247
+ loadBlogPost: () => loadBlogPost,
239
248
  loadPolicy: () => loadPolicy,
240
249
  locales: () => locales,
241
250
  matchLocale: () => matchLocale,
@@ -458,6 +467,168 @@ function getPolicyLocales(slug, contentDir = "src/content/policies") {
458
467
  return locales2;
459
468
  }
460
469
 
470
+ // src/lib/content/blog.ts
471
+ var import_fs2 = __toESM(require("fs"));
472
+ var import_path2 = __toESM(require("path"));
473
+ var import_rsc2 = require("next-mdx-remote/rsc");
474
+ var import_rehype_slug2 = __toESM(require("rehype-slug"));
475
+ function resolveDir(contentDir) {
476
+ return import_path2.default.isAbsolute(contentDir) ? contentDir : import_path2.default.join(process.cwd(), contentDir);
477
+ }
478
+ var REQUIRED_FIELDS = ["title", "excerpt", "author", "date", "tags"];
479
+ async function loadBlogPost(slug, locale, contentDir = "src/content/blog") {
480
+ const dir = resolveDir(contentDir);
481
+ const filePath = import_path2.default.join(dir, `${slug}.${locale}.md`);
482
+ if (!import_fs2.default.existsSync(filePath)) {
483
+ throw new Error(
484
+ `Blog post file not found: ${slug}.${locale}.md in ${contentDir}
485
+ Looking for: ${filePath}`
486
+ );
487
+ }
488
+ const source = import_fs2.default.readFileSync(filePath, "utf8");
489
+ const { content, frontmatter } = await (0, import_rsc2.compileMDX)({
490
+ source,
491
+ options: {
492
+ parseFrontmatter: true,
493
+ mdxOptions: {
494
+ rehypePlugins: [import_rehype_slug2.default]
495
+ }
496
+ }
497
+ });
498
+ for (const field of REQUIRED_FIELDS) {
499
+ if (!frontmatter[field]) {
500
+ throw new Error(
501
+ `Blog post ${slug}.${locale}.md is missing required frontmatter field: ${field}`
502
+ );
503
+ }
504
+ }
505
+ return {
506
+ content,
507
+ metadata: frontmatter,
508
+ slug,
509
+ locale
510
+ };
511
+ }
512
+ function getBlogPostSlugs(contentDir = "src/content/blog") {
513
+ const dir = resolveDir(contentDir);
514
+ if (!import_fs2.default.existsSync(dir)) {
515
+ console.warn(`Blog directory not found: ${dir}`);
516
+ return [];
517
+ }
518
+ const files = import_fs2.default.readdirSync(dir);
519
+ const slugs = Array.from(
520
+ new Set(
521
+ files.filter((file) => file.endsWith(".md") || file.endsWith(".mdx")).map((file) => file.replace(/\.[a-z]{2}(-[A-Z]{2})?\.mdx?$/, ""))
522
+ )
523
+ );
524
+ return slugs;
525
+ }
526
+ async function getAllBlogPosts(locale, contentDir = "src/content/blog") {
527
+ const slugs = getBlogPostSlugs(contentDir);
528
+ const posts = await Promise.all(
529
+ slugs.map(async (slug) => {
530
+ try {
531
+ const post = await loadBlogPost(slug, locale, contentDir);
532
+ return {
533
+ slug: post.slug,
534
+ locale: post.locale,
535
+ metadata: post.metadata
536
+ };
537
+ } catch (error) {
538
+ console.warn(`Skipping ${slug} for locale ${locale}:`, error);
539
+ return null;
540
+ }
541
+ })
542
+ );
543
+ return posts.filter((p) => p !== null).sort((a, b) => new Date(b.metadata.date).getTime() - new Date(a.metadata.date).getTime());
544
+ }
545
+ function getBlogPostLocales(slug, contentDir = "src/content/blog") {
546
+ const dir = resolveDir(contentDir);
547
+ if (!import_fs2.default.existsSync(dir)) {
548
+ return [];
549
+ }
550
+ const files = import_fs2.default.readdirSync(dir);
551
+ const locales2 = files.filter((file) => {
552
+ const pattern = new RegExp(`^${slug}\\.[a-z]{2}(-[A-Z]{2})?\\.mdx?$`);
553
+ return pattern.test(file);
554
+ }).map((file) => {
555
+ const match = file.match(/\.([a-z]{2}(-[A-Z]{2})?)\.mdx?$/);
556
+ return match ? match[1] : null;
557
+ }).filter((locale) => locale !== null);
558
+ return locales2;
559
+ }
560
+ async function getBlogPostsByTag(tag, locale, contentDir = "src/content/blog") {
561
+ const posts = await getAllBlogPosts(locale, contentDir);
562
+ return posts.filter((p) => p.metadata.tags.includes(tag));
563
+ }
564
+ async function getFeaturedBlogPosts(locale, contentDir = "src/content/blog") {
565
+ const posts = await getAllBlogPosts(locale, contentDir);
566
+ return posts.filter((p) => p.metadata.featured === true);
567
+ }
568
+ async function getRelatedBlogPosts(slug, locale, count = 3, contentDir = "src/content/blog") {
569
+ const allPosts = await getAllBlogPosts(locale, contentDir);
570
+ const sourcePost = allPosts.find((p) => p.slug === slug);
571
+ if (!sourcePost) return [];
572
+ const sourceTags = new Set(sourcePost.metadata.tags);
573
+ const candidates = allPosts.filter((p) => p.slug !== slug);
574
+ const scored = candidates.map((post) => ({
575
+ post,
576
+ sharedTags: post.metadata.tags.filter((t) => sourceTags.has(t)).length
577
+ }));
578
+ return scored.filter((s) => s.sharedTags > 0).sort((a, b) => {
579
+ if (b.sharedTags !== a.sharedTags) return b.sharedTags - a.sharedTags;
580
+ return new Date(b.post.metadata.date).getTime() - new Date(a.post.metadata.date).getTime();
581
+ }).slice(0, count).map((s) => s.post);
582
+ }
583
+ async function getAllTags(locale, contentDir = "src/content/blog") {
584
+ const posts = await getAllBlogPosts(locale, contentDir);
585
+ const counts = /* @__PURE__ */ new Map();
586
+ for (const post of posts) {
587
+ for (const tag of post.metadata.tags) {
588
+ counts.set(tag, (counts.get(tag) ?? 0) + 1);
589
+ }
590
+ }
591
+ return Array.from(counts.entries()).map(([tag, count]) => ({ tag, count })).sort((a, b) => b.count - a.count);
592
+ }
593
+
594
+ // src/lib/content/rss.ts
595
+ var MAX_ITEMS = 20;
596
+ async function generateBlogRssFeed(options) {
597
+ const { siteUrl, siteName, locale, contentDir } = options;
598
+ const description = typeof options.description === "string" ? options.description : getLocalizedString(options.description, locale);
599
+ const posts = await getAllBlogPosts(locale, contentDir);
600
+ const items = posts.slice(0, MAX_ITEMS);
601
+ const itemsXml = items.map((post) => {
602
+ const link = `${siteUrl}/${locale}/blog/${post.slug}`;
603
+ const pubDate = (/* @__PURE__ */ new Date(`${post.metadata.date}T12:00:00Z`)).toUTCString();
604
+ const categories = post.metadata.tags.map((tag) => ` <category>${escapeXml(tag)}</category>`).join("\n");
605
+ return ` <item>
606
+ <title>${escapeXml(post.metadata.title)}</title>
607
+ <description>${escapeXml(post.metadata.excerpt)}</description>
608
+ <link>${escapeXml(link)}</link>
609
+ <guid>${escapeXml(link)}</guid>
610
+ <pubDate>${pubDate}</pubDate>
611
+ <author>${escapeXml(post.metadata.author)}</author>
612
+ ${categories}
613
+ </item>`;
614
+ }).join("\n");
615
+ return `<?xml version="1.0" encoding="UTF-8"?>
616
+ <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
617
+ <channel>
618
+ <title>${escapeXml(siteName)}</title>
619
+ <description>${escapeXml(description)}</description>
620
+ <link>${escapeXml(siteUrl)}</link>
621
+ <language>${escapeXml(locale)}</language>
622
+ <lastBuildDate>${(/* @__PURE__ */ new Date()).toUTCString()}</lastBuildDate>
623
+ <atom:link href="${escapeXml(siteUrl)}/${escapeXml(locale)}/blog/feed.xml" rel="self" type="application/rss+xml"/>
624
+ ${itemsXml}
625
+ </channel>
626
+ </rss>`;
627
+ }
628
+ function escapeXml(str) {
629
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
630
+ }
631
+
461
632
  // src/lib/navigation/utils.ts
462
633
  function getNavigationString(localizedString, locale) {
463
634
  return localizedString[locale] || localizedString["en"] || "";
@@ -535,7 +706,7 @@ var defaultSlugTranslations = {
535
706
  "/anti-spam-policy": "/politique-anti-pourriel"
536
707
  }
537
708
  };
538
- function translateSlug(path2, fromLocale, toLocale, customTranslations) {
709
+ function translateSlug(path3, fromLocale, toLocale, customTranslations) {
539
710
  let configTranslations = {};
540
711
  try {
541
712
  const { getI18nConfig: getI18nConfig2 } = (init_config(), __toCommonJS(config_exports));
@@ -550,17 +721,17 @@ function translateSlug(path2, fromLocale, toLocale, customTranslations) {
550
721
  );
551
722
  const translationMap = translations[fromLocale];
552
723
  if (!translationMap) {
553
- return path2;
724
+ return path3;
554
725
  }
555
- if (translationMap[path2]) {
556
- return translationMap[path2];
726
+ if (translationMap[path3]) {
727
+ return translationMap[path3];
557
728
  }
558
729
  for (const [fromSlug, toSlug] of Object.entries(translationMap)) {
559
- if (path2.startsWith(fromSlug + "/")) {
560
- return path2.replace(fromSlug, toSlug);
730
+ if (path3.startsWith(fromSlug + "/")) {
731
+ return path3.replace(fromSlug, toSlug);
561
732
  }
562
733
  }
563
- return path2;
734
+ return path3;
564
735
  }
565
736
  function mergeTranslations(...translations) {
566
737
  const merged = {};
@@ -1389,7 +1560,7 @@ function generateSitemap(config) {
1389
1560
  const urlEntries = entries.map((entry) => {
1390
1561
  const parts = [];
1391
1562
  parts.push(`${indent}<url>${newline}`);
1392
- parts.push(`${indent}${indent}<loc>${escapeXml(entry.url)}</loc>${newline}`);
1563
+ parts.push(`${indent}${indent}<loc>${escapeXml2(entry.url)}</loc>${newline}`);
1393
1564
  if (entry.lastModified) {
1394
1565
  const date = entry.lastModified instanceof Date ? entry.lastModified.toISOString() : entry.lastModified;
1395
1566
  parts.push(`${indent}${indent}<lastmod>${date}</lastmod>${newline}`);
@@ -1404,9 +1575,9 @@ function generateSitemap(config) {
1404
1575
  if (entry.alternates && entry.alternates.length > 0) {
1405
1576
  entry.alternates.forEach((alternate) => {
1406
1577
  parts.push(
1407
- `${indent}${indent}<xhtml:link rel="alternate" hreflang="${escapeXml(
1578
+ `${indent}${indent}<xhtml:link rel="alternate" hreflang="${escapeXml2(
1408
1579
  alternate.hreflang
1409
- )}" href="${escapeXml(alternate.href)}" />${newline}`
1580
+ )}" href="${escapeXml2(alternate.href)}" />${newline}`
1410
1581
  );
1411
1582
  });
1412
1583
  }
@@ -1423,8 +1594,8 @@ function generateSitemap(config) {
1423
1594
  ].join("");
1424
1595
  return xml;
1425
1596
  }
1426
- function createMultiLanguageEntries(baseUrl, path2, locales2, defaultLocale2, options) {
1427
- const cleanPath = path2.startsWith("/") ? path2 : `/${path2}`;
1597
+ function createMultiLanguageEntries(baseUrl, path3, locales2, defaultLocale2, options) {
1598
+ const cleanPath = path3.startsWith("/") ? path3 : `/${path3}`;
1428
1599
  return locales2.map((locale) => {
1429
1600
  const alternates = locales2.map((altLocale) => ({
1430
1601
  hreflang: altLocale,
@@ -1443,15 +1614,15 @@ function createMultiLanguageEntries(baseUrl, path2, locales2, defaultLocale2, op
1443
1614
  };
1444
1615
  });
1445
1616
  }
1446
- function createSitemapEntry(baseUrl, path2, options) {
1447
- const cleanPath = path2.startsWith("/") ? path2 : `/${path2}`;
1448
- const url = path2 === "/" ? baseUrl : `${baseUrl}${cleanPath}`;
1617
+ function createSitemapEntry(baseUrl, path3, options) {
1618
+ const cleanPath = path3.startsWith("/") ? path3 : `/${path3}`;
1619
+ const url = path3 === "/" ? baseUrl : `${baseUrl}${cleanPath}`;
1449
1620
  return {
1450
1621
  url,
1451
1622
  ...options
1452
1623
  };
1453
1624
  }
1454
- function escapeXml(unsafe) {
1625
+ function escapeXml2(unsafe) {
1455
1626
  return unsafe.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
1456
1627
  }
1457
1628
  function validateSitemapEntry(entry) {
@@ -1642,14 +1813,21 @@ function FeaturesGrid({ categories, className = "", locale = "en" }) {
1642
1813
  formatNumber,
1643
1814
  formatRelativeTime,
1644
1815
  generateArticleMetadata,
1816
+ generateBlogRssFeed,
1645
1817
  generateDesignTokens,
1646
1818
  generateMetadata,
1647
1819
  generateSitemap,
1648
1820
  generateThemeCSS,
1821
+ getAllBlogPosts,
1649
1822
  getAllPolicies,
1823
+ getAllTags,
1650
1824
  getAlternateLocales,
1825
+ getBlogPostLocales,
1826
+ getBlogPostSlugs,
1827
+ getBlogPostsByTag,
1651
1828
  getDefaultLocale,
1652
1829
  getErrorMessage,
1830
+ getFeaturedBlogPosts,
1653
1831
  getFontConfig,
1654
1832
  getFontVariables,
1655
1833
  getI18nConfig,
@@ -1666,6 +1844,7 @@ function FeaturesGrid({ categories, className = "", locale = "en" }) {
1666
1844
  getNavigationString,
1667
1845
  getPolicyLocales,
1668
1846
  getPolicySlugs,
1847
+ getRelatedBlogPosts,
1669
1848
  getRelativeTime,
1670
1849
  getRtlLocales,
1671
1850
  getTextDirection,
@@ -1674,6 +1853,7 @@ function FeaturesGrid({ categories, className = "", locale = "en" }) {
1674
1853
  isLocaleDetectionEnabled,
1675
1854
  isRtlLocale,
1676
1855
  isSupportedLocale,
1856
+ loadBlogPost,
1677
1857
  loadPolicy,
1678
1858
  locales,
1679
1859
  matchLocale,