@fayz-ai/plugin-blog 0.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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/dist/components/AuthorAvatar.d.ts +7 -0
  3. package/dist/components/AuthorAvatar.d.ts.map +1 -0
  4. package/dist/components/BlogList.d.ts +6 -0
  5. package/dist/components/BlogList.d.ts.map +1 -0
  6. package/dist/components/PostCard.d.ts +10 -0
  7. package/dist/components/PostCard.d.ts.map +1 -0
  8. package/dist/components/PostDetail.d.ts +7 -0
  9. package/dist/components/PostDetail.d.ts.map +1 -0
  10. package/dist/config.d.ts +38 -0
  11. package/dist/config.d.ts.map +1 -0
  12. package/dist/context.d.ts +14 -0
  13. package/dist/context.d.ts.map +1 -0
  14. package/dist/data/index.d.ts +5 -0
  15. package/dist/data/index.d.ts.map +1 -0
  16. package/dist/data/mock.d.ts +18 -0
  17. package/dist/data/mock.d.ts.map +1 -0
  18. package/dist/data/supabase.d.ts +3 -0
  19. package/dist/data/supabase.d.ts.map +1 -0
  20. package/dist/data/types.d.ts +10 -0
  21. package/dist/data/types.d.ts.map +1 -0
  22. package/dist/hooks/index.d.ts +5 -0
  23. package/dist/hooks/index.d.ts.map +1 -0
  24. package/dist/hooks/useBlogPosts.d.ts +12 -0
  25. package/dist/hooks/useBlogPosts.d.ts.map +1 -0
  26. package/dist/hooks/usePost.d.ts +9 -0
  27. package/dist/hooks/usePost.d.ts.map +1 -0
  28. package/dist/index.cjs +372 -0
  29. package/dist/index.cjs.map +1 -0
  30. package/dist/index.d.ts +29 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +360 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/seo.d.ts +16 -0
  35. package/dist/seo.d.ts.map +1 -0
  36. package/dist/types.d.ts +38 -0
  37. package/dist/types.d.ts.map +1 -0
  38. package/package.json +51 -0
  39. package/src/components/AuthorAvatar.tsx +20 -0
  40. package/src/components/BlogList.tsx +54 -0
  41. package/src/components/PostCard.tsx +44 -0
  42. package/src/components/PostDetail.tsx +108 -0
  43. package/src/config.ts +50 -0
  44. package/src/context.tsx +23 -0
  45. package/src/data/index.ts +4 -0
  46. package/src/data/mock.ts +71 -0
  47. package/src/data/supabase.ts +28 -0
  48. package/src/data/types.ts +10 -0
  49. package/src/hooks/index.ts +4 -0
  50. package/src/hooks/useBlogPosts.ts +47 -0
  51. package/src/hooks/usePost.ts +46 -0
  52. package/src/index.ts +80 -0
  53. package/src/seo.ts +63 -0
  54. package/src/types.ts +45 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts","../src/data/mock.ts","../src/data/supabase.ts","../src/context.tsx","../src/hooks/useBlogPosts.ts","../src/components/AuthorAvatar.tsx","../src/components/PostCard.tsx","../src/seo.ts","../src/components/BlogList.tsx","../src/hooks/usePost.ts","../src/components/PostDetail.tsx","../src/index.ts"],"names":["createContext","jsx","useContext","useState","useEffect","jsxs","Link","Clock","absoluteUrl","organizationJsonLd","breadcrumbJsonLd","useSeo","useParams","Fragment","createSafeDataProvider","createElement"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { PluginScope, VerticalId } from '@fayz-ai/core'\nimport type { BlogDataProvider } from './data/types'\nimport type { BlogSeed } from './data/mock'\nimport type { BlogAuthor } from './types'\n\nexport interface BlogPluginOptions {\n /** Base path the plugin mounts its public routes under. Default '/blog'. */\n basePath?: string\n /** Seed posts for the mock provider (used until a real backend is wired). */\n seed?: BlogSeed\n /** Default byline author, applied to any post that doesn't specify its own. */\n defaultAuthor?: BlogAuthor\n /** Publisher/brand name — used for SEO (og:site_name, JSON-LD publisher). */\n siteName?: string\n /** Inject a custom provider (e.g. a CMS). Overrides the safe mock/Supabase resolver. */\n dataProvider?: BlogDataProvider\n /** Section eyebrow / labels for the default list screen. */\n labels?: {\n /** Title shown at the top of the /blog list screen. */\n listTitle?: string\n /** Subtitle under the list title. */\n listSubtitle?: string\n /** \"Back to blog\" link label on the detail screen. */\n backToList?: string\n }\n scope?: PluginScope\n verticalId?: VerticalId\n}\n\nexport interface ResolvedBlogConfig {\n basePath: string\n siteName: string\n labels: {\n listTitle: string\n listSubtitle: string\n backToList: string\n }\n}\n\nexport function resolveConfig(options?: BlogPluginOptions): ResolvedBlogConfig {\n return {\n basePath: options?.basePath ?? '/blog',\n siteName: options?.siteName ?? '',\n labels: {\n listTitle: options?.labels?.listTitle ?? 'Blog',\n listSubtitle: options?.labels?.listSubtitle ?? '',\n backToList: options?.labels?.backToList ?? '← Voltar ao blog',\n },\n }\n}\n","import type { BlogDataProvider } from './types'\nimport type { BlogPost, BlogListQuery, BlogAuthor } from '../types'\n\n/** Slugify a title when a seed post omits an explicit slug. */\nfunction slugify(input: string): string {\n return input\n .normalize('NFD')\n // Drop combining accent marks (and any other non-ASCII) so \"saúde\" -> \"saude\".\n .replace(/[^\\x00-\\x7f]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/(^-|-$)/g, '')\n .slice(0, 80)\n}\n\n/** Seed post — same shape as BlogPost but slug/body/author may be derived. */\nexport type BlogSeedPost = Omit<BlogPost, 'slug' | 'body' | 'author'> & {\n slug?: string\n body?: string\n author?: BlogAuthor\n}\n\nexport interface BlogSeed {\n posts: BlogSeedPost[]\n}\n\nexport interface MockBlogProviderOptions {\n seed?: BlogSeed\n /** Fallback byline used for posts without their own author. */\n defaultAuthor?: BlogAuthor\n}\n\nconst FALLBACK_AUTHOR: BlogAuthor = { name: 'Equipe' }\n\nfunction normalize(post: BlogSeedPost, defaultAuthor: BlogAuthor): BlogPost {\n return {\n ...post,\n slug: post.slug ?? slugify(post.title),\n body: post.body ?? post.excerpt,\n author: post.author ?? defaultAuthor,\n }\n}\n\n/** A built-in fallback post so an unseeded plugin still renders something. */\nconst FALLBACK: BlogSeedPost[] = [\n {\n title: 'Bem-vindo ao blog',\n excerpt: 'Este é um artigo de exemplo servido pelo plugin de blog do Fayz.',\n tag: 'Novidades',\n readTime: '2 min',\n date: 'Hoje',\n image: 'https://images.unsplash.com/photo-1499750310107-5fef28a66643?w=800&q=80',\n },\n]\n\nexport function createMockBlogProvider(options?: MockBlogProviderOptions): BlogDataProvider {\n const defaultAuthor = options?.defaultAuthor ?? FALLBACK_AUTHOR\n const posts: BlogPost[] = (options?.seed?.posts ?? FALLBACK).map((p) => normalize(p, defaultAuthor))\n\n return {\n async listPosts(query?: BlogListQuery): Promise<BlogPost[]> {\n let result = posts\n if (query?.tag) result = result.filter((p) => p.tag === query.tag)\n if (query?.limit != null) result = result.slice(0, query.limit)\n return result\n },\n async getPost(slug: string): Promise<BlogPost | null> {\n return posts.find((p) => p.slug === slug) ?? null\n },\n }\n}\n","import type { BlogDataProvider } from './types'\nimport type { BlogPost, BlogListQuery } from '../types'\n\n// ---------------------------------------------------------------------------\n// Supabase-backed blog provider — STUB.\n//\n// Deferred (Phase 2): read from a `blog_posts` table (tenant-scoped, RLS) with\n// columns mirroring BlogPost (slug, title, excerpt, body, tag, read_time, date,\n// image, author). Wiring this up is a pure provider swap — no hook/component\n// change — because everything downstream depends only on BlogDataProvider.\n//\n// Until then this throws, which makes createSafeDataProvider fall back to the\n// mock provider whenever no Supabase client is configured. If a client IS\n// configured but this is still a stub, surface a clear error.\n// ---------------------------------------------------------------------------\n\nexport function createSupabaseBlogProvider(): BlogDataProvider {\n const notImplemented = (): never => {\n throw new Error(\n '[plugin-blog] Supabase provider not implemented yet — deferred to Phase 2. ' +\n 'Run on the mock/seed provider (no Supabase client configured) for now.',\n )\n }\n return {\n listPosts: (_query?: BlogListQuery): Promise<BlogPost[]> => notImplemented(),\n getPost: (_slug: string): Promise<BlogPost | null> => notImplemented(),\n }\n}\n","import { createContext, useContext, type ReactNode } from 'react'\nimport type { BlogDataProvider } from './data/types'\nimport type { ResolvedBlogConfig } from './config'\n\nexport interface BlogContextValue {\n provider: BlogDataProvider\n config: ResolvedBlogConfig\n}\n\nconst BlogContext = createContext<BlogContextValue | null>(null)\n\nexport function BlogProvider({ value, children }: { value: BlogContextValue; children: ReactNode }) {\n return <BlogContext.Provider value={value}>{children}</BlogContext.Provider>\n}\n\n/** Read the blog provider + config. Throws if used outside a BlogProvider. */\nexport function useBlogContext(): BlogContextValue {\n const ctx = useContext(BlogContext)\n if (!ctx) {\n throw new Error('[plugin-blog] useBlogContext must be used within <BlogProvider>. Mount the plugin Provider at the app root.')\n }\n return ctx\n}\n","import { useEffect, useState } from 'react'\nimport { useBlogContext } from '../context'\nimport type { BlogPost, BlogListQuery } from '../types'\n\nexport interface UseBlogPostsResult {\n posts: BlogPost[]\n loading: boolean\n error: Error | null\n}\n\n/**\n * Fetch the post list from the active blog provider. The host maps `posts` into\n * its own card markup; the plugin never dictates presentation.\n */\nexport function useBlogPosts(query?: BlogListQuery): UseBlogPostsResult {\n const { provider } = useBlogContext()\n const [posts, setPosts] = useState<BlogPost[]>([])\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n const limit = query?.limit\n const tag = query?.tag\n\n useEffect(() => {\n let active = true\n setLoading(true)\n provider\n .listPosts({ limit, tag })\n .then((result) => {\n if (!active) return\n setPosts(result)\n setError(null)\n })\n .catch((err) => {\n if (!active) return\n setError(err instanceof Error ? err : new Error(String(err)))\n })\n .finally(() => {\n if (active) setLoading(false)\n })\n return () => {\n active = false\n }\n }, [provider, limit, tag])\n\n return { posts, loading, error }\n}\n","import type { BlogAuthor } from '../types'\n\n/** Round author avatar with a name-initial fallback (token-only). */\nexport function AuthorAvatar({ author, className = 'h-8 w-8 text-xs' }: { author: BlogAuthor; className?: string }) {\n if (author.avatarUrl) {\n return (\n <img\n src={author.avatarUrl}\n alt={author.name}\n loading=\"lazy\"\n className={`shrink-0 rounded-full object-cover ${className}`}\n />\n )\n }\n return (\n <span className={`flex shrink-0 items-center justify-center rounded-full bg-accent font-semibold text-primary ${className}`}>\n {author.name.charAt(0).toUpperCase()}\n </span>\n )\n}\n","import { Link } from 'react-router-dom'\nimport { Clock } from 'lucide-react'\nimport type { BlogPost } from '../types'\nimport { AuthorAvatar } from './AuthorAvatar'\n\n/**\n * Default post card — token-only so it inherits the host theme. Optional: a host\n * with bespoke card markup can ignore this and map `useBlogPosts()` itself.\n */\nexport function PostCard({ post, basePath = '/blog' }: { post: BlogPost; basePath?: string }) {\n return (\n <Link\n to={`${basePath}/${post.slug}`}\n className=\"group flex flex-col rounded-2xl border border-border bg-card overflow-hidden shadow-sm hover:shadow-lg hover:-translate-y-1 transition-all duration-300\"\n >\n <div className=\"overflow-hidden h-48\">\n <img\n src={post.image}\n alt={post.title}\n loading=\"lazy\"\n className=\"h-full w-full object-cover transition-transform duration-500 group-hover:scale-105\"\n />\n </div>\n <div className=\"flex flex-col flex-1 p-6\">\n <span className=\"inline-block rounded-full bg-accent px-3 py-0.5 text-xs font-semibold text-primary mb-3 w-fit\">\n {post.tag}\n </span>\n <h3 className=\"font-heading text-lg font-semibold text-foreground leading-snug mb-3 group-hover:text-primary transition-colors duration-200\">\n {post.title}\n </h3>\n <p className=\"text-muted-foreground text-sm leading-relaxed flex-1 mb-4\">{post.excerpt}</p>\n <div className=\"flex items-center gap-2.5 pt-4 border-t border-border\">\n <AuthorAvatar author={post.author} className=\"h-7 w-7 text-xs\" />\n <div className=\"min-w-0\">\n <p className=\"truncate text-xs font-semibold text-foreground\">{post.author.name}</p>\n <p className=\"flex items-center gap-1 text-[11px] text-muted-foreground\">\n {post.date}<span aria-hidden>·</span><Clock className=\"h-3 w-3\" />{post.readTime}\n </p>\n </div>\n </div>\n </div>\n </Link>\n )\n}\n","import { absoluteUrl, breadcrumbJsonLd, organizationJsonLd } from '@fayz-ai/core'\nimport type { BlogPost } from './types'\n\n// ---------------------------------------------------------------------------\n// Blog-specific schema.org builders. The head-manager mechanics (useSeo /\n// applySeo / meta+OG+Twitter) live in @fayz-ai/core — this only owns the\n// domain shapes (BlogPosting, Blog) that plug into core's SeoConfig.jsonLd.\n// ---------------------------------------------------------------------------\n\n/** Absolute canonical URL for a post. */\nexport function postUrl(basePath: string, slug: string): string {\n return absoluteUrl(`${basePath}/${slug}`)\n}\n\n/** schema.org BlogPosting + BreadcrumbList for an article page. */\nexport function articleJsonLd(post: BlogPost, opts: { basePath: string; siteName: string }): Array<Record<string, unknown>> {\n const url = postUrl(opts.basePath, post.slug)\n const posting: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': 'BlogPosting',\n headline: post.title,\n description: post.excerpt,\n image: post.image ? [post.image] : undefined,\n articleSection: post.tag,\n mainEntityOfPage: { '@type': 'WebPage', '@id': url },\n author: {\n '@type': 'Person',\n name: post.author.name,\n ...(post.author.role ? { jobTitle: post.author.role } : {}),\n },\n ...(post.publishedAt ? { datePublished: post.publishedAt, dateModified: post.publishedAt } : {}),\n ...(opts.siteName ? { publisher: organizationJsonLd(opts.siteName) } : {}),\n }\n const breadcrumb = breadcrumbJsonLd([\n { name: 'Blog', url: absoluteUrl(opts.basePath) },\n { name: post.title, url },\n ])\n return [posting, breadcrumb]\n}\n\n/** schema.org Blog + post list for the listing page. */\nexport function blogJsonLd(posts: BlogPost[], opts: { basePath: string; siteName: string; title: string }): Array<Record<string, unknown>> {\n return [\n {\n '@context': 'https://schema.org',\n '@type': 'Blog',\n name: opts.title,\n url: absoluteUrl(opts.basePath),\n ...(opts.siteName ? { publisher: organizationJsonLd(opts.siteName) } : {}),\n blogPost: posts.map((p) => ({\n '@type': 'BlogPosting',\n headline: p.title,\n url: postUrl(opts.basePath, p.slug),\n image: p.image,\n author: { '@type': 'Person', name: p.author.name },\n ...(p.publishedAt ? { datePublished: p.publishedAt } : {}),\n })),\n },\n ]\n}\n\n// Re-export the generic hook so blog components import SEO from one place.\nexport { useSeo } from '@fayz-ai/core'\n","import { useBlogPosts } from '../hooks/useBlogPosts'\nimport { useBlogContext } from '../context'\nimport { PostCard } from './PostCard'\nimport { useSeo, blogJsonLd } from '../seo'\n\n/**\n * Full /blog list screen. Rendered inside the host's chrome (Header/Footer),\n * so it only owns the section body — token-only for theme inheritance.\n */\nexport function BlogList() {\n const { config } = useBlogContext()\n const { posts, loading, error } = useBlogPosts()\n\n const listTitle = config.labels.listSubtitle || config.labels.listTitle\n useSeo({\n title: `${listTitle}${config.siteName ? ` · ${config.siteName}` : ''}`,\n description: config.labels.listSubtitle || `Artigos do blog${config.siteName ? ` da ${config.siteName}` : ''}.`,\n canonical: `${typeof window !== 'undefined' ? window.location.origin : ''}${config.basePath}`,\n type: 'website',\n siteName: config.siteName,\n jsonLd: posts.length ? blogJsonLd(posts, { basePath: config.basePath, siteName: config.siteName, title: listTitle }) : undefined,\n })\n\n return (\n <section className=\"py-20 bg-background\">\n <div className=\"container mx-auto px-6\">\n <div className=\"mb-12 max-w-2xl\">\n <span className=\"inline-block rounded-full bg-accent px-4 py-1 text-xs font-semibold uppercase tracking-widest text-primary mb-4\">\n {config.labels.listTitle}\n </span>\n {config.labels.listSubtitle ? (\n <h1 className=\"font-heading text-4xl md:text-5xl font-bold text-foreground\">\n {config.labels.listSubtitle}\n </h1>\n ) : null}\n </div>\n\n {loading ? (\n <p className=\"text-muted-foreground\">Carregando artigos…</p>\n ) : error ? (\n <p className=\"text-muted-foreground\">Não foi possível carregar os artigos.</p>\n ) : posts.length === 0 ? (\n <p className=\"text-muted-foreground\">Nenhum artigo publicado ainda.</p>\n ) : (\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-6\">\n {posts.map((post) => (\n <PostCard key={post.slug} post={post} basePath={config.basePath} />\n ))}\n </div>\n )}\n </div>\n </section>\n )\n}\n","import { useEffect, useState } from 'react'\nimport { useBlogContext } from '../context'\nimport type { BlogPost } from '../types'\n\nexport interface UsePostResult {\n post: BlogPost | null\n loading: boolean\n error: Error | null\n}\n\n/** Fetch a single post by slug from the active blog provider. */\nexport function usePost(slug: string | undefined): UsePostResult {\n const { provider } = useBlogContext()\n const [post, setPost] = useState<BlogPost | null>(null)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n useEffect(() => {\n let active = true\n if (!slug) {\n setPost(null)\n setLoading(false)\n return\n }\n setLoading(true)\n provider\n .getPost(slug)\n .then((result) => {\n if (!active) return\n setPost(result)\n setError(null)\n })\n .catch((err) => {\n if (!active) return\n setError(err instanceof Error ? err : new Error(String(err)))\n })\n .finally(() => {\n if (active) setLoading(false)\n })\n return () => {\n active = false\n }\n }, [provider, slug])\n\n return { post, loading, error }\n}\n","import { Link, useParams } from 'react-router-dom'\nimport { Clock } from 'lucide-react'\nimport { usePost } from '../hooks/usePost'\nimport { useBlogContext } from '../context'\nimport { AuthorAvatar } from './AuthorAvatar'\nimport { useSeo, articleJsonLd, postUrl } from '../seo'\n\n/**\n * /blog/:slug detail screen. Reads the slug from the router and renders the\n * article inside the host chrome. Token-only for theme inheritance. Sets rich\n * SEO/JSON-LD per article (BlogPosting + BreadcrumbList).\n */\nexport function PostDetail() {\n const { config } = useBlogContext()\n const { slug } = useParams<{ slug: string }>()\n const { post, loading, error } = usePost(slug)\n\n useSeo(\n post\n ? {\n title: `${post.title}${config.siteName ? ` · ${config.siteName}` : ''}`,\n description: post.excerpt,\n canonical: postUrl(config.basePath, post.slug),\n image: post.image,\n type: 'article',\n siteName: config.siteName,\n jsonLd: articleJsonLd(post, { basePath: config.basePath, siteName: config.siteName }),\n }\n : { title: config.siteName || 'Blog' },\n )\n\n const paragraphs = post?.body ? post.body.split(/\\n\\s*\\n/).map((p) => p.trim()).filter(Boolean) : []\n\n return (\n <article className=\"py-16 bg-background\">\n <div className=\"container mx-auto px-6 max-w-3xl\">\n <Link to={config.basePath} className=\"text-sm font-medium text-primary hover:underline\">\n {config.labels.backToList}\n </Link>\n\n {loading ? (\n <p className=\"mt-10 text-muted-foreground\">Carregando…</p>\n ) : error || !post ? (\n <div className=\"mt-10\">\n <h1 className=\"font-heading text-3xl font-bold text-foreground\">Artigo não encontrado</h1>\n <p className=\"mt-2 text-muted-foreground\">Este artigo pode ter sido movido ou removido.</p>\n </div>\n ) : (\n <>\n <h1 className=\"mt-8 font-heading text-4xl md:text-5xl font-bold text-foreground leading-tight\">\n {post.title}\n </h1>\n\n {/* Medium-style byline */}\n <div className=\"mt-6 flex items-center gap-3\">\n <AuthorAvatar author={post.author} className=\"h-11 w-11 text-base\" />\n <div>\n <p className=\"font-semibold text-foreground\">{post.author.name}</p>\n <p className=\"flex flex-wrap items-center gap-1.5 text-sm text-muted-foreground\">\n {post.author.role ? <span>{post.author.role}<span aria-hidden> · </span></span> : null}\n <time dateTime={post.publishedAt}>{post.date}</time>\n <span className=\"flex items-center gap-1\"><Clock className=\"h-3 w-3\" />{post.readTime} de leitura</span>\n </p>\n </div>\n </div>\n\n <div className=\"mt-8 overflow-hidden rounded-2xl\">\n <img src={post.image} alt={post.title} className=\"w-full object-cover\" loading=\"lazy\" />\n </div>\n\n <div className=\"mt-8 space-y-5\">\n <p className=\"text-lg text-foreground/90 leading-relaxed font-medium\">{post.excerpt}</p>\n {paragraphs.map((para, i) => (\n <p key={i} className=\"text-muted-foreground leading-relaxed\">\n {para}\n </p>\n ))}\n </div>\n\n {/* Category tag — at the bottom, Medium-style */}\n <div className=\"mt-10 flex flex-wrap items-center gap-2 border-t border-border pt-6\">\n <span className=\"text-sm text-muted-foreground\">Categoria:</span>\n <Link\n to={config.basePath}\n className=\"rounded-full bg-accent px-3 py-1 text-xs font-semibold text-primary transition-colors hover:bg-accent/70\"\n >\n {post.tag}\n </Link>\n </div>\n\n {/* About the author */}\n {post.author.bio ? (\n <div className=\"mt-8 flex items-start gap-4 rounded-2xl border border-border bg-muted/40 p-6\">\n <AuthorAvatar author={post.author} className=\"h-14 w-14 text-lg\" />\n <div>\n <p className=\"text-xs font-semibold uppercase tracking-wide text-muted-foreground\">Escrito por</p>\n <p className=\"font-heading text-lg font-bold text-foreground\">{post.author.name}</p>\n {post.author.role ? <p className=\"text-sm text-primary\">{post.author.role}</p> : null}\n <p className=\"mt-2 text-sm text-muted-foreground leading-relaxed\">{post.author.bio}</p>\n </div>\n </div>\n ) : null}\n </>\n )}\n </div>\n </article>\n )\n}\n","import { createElement, type FC, type ReactNode } from 'react'\nimport type { PluginManifest } from '@fayz-ai/core'\nimport { createSafeDataProvider } from '@fayz-ai/core'\nimport { resolveConfig, type BlogPluginOptions } from './config'\nimport { createMockBlogProvider } from './data/mock'\nimport { createSupabaseBlogProvider } from './data/supabase'\nimport { BlogProvider, type BlogContextValue } from './context'\nimport { BlogList } from './components/BlogList'\nimport { PostDetail } from './components/PostDetail'\n\n// ---------------------------------------------------------------------------\n// @fayz-ai/plugin-blog — website-surface blog/articles plugin.\n//\n// First real consumer of scaffolds:['website']. Ships public routes (/blog,\n// /blog/:slug) + data hooks (useBlogPosts/usePost). A bespoke host site feeds\n// its own card markup from the hooks and mounts the routes inside its chrome.\n// See createBlogPlugin's return: a { manifest, Provider } bundle the host reads\n// uniformly (manifest.routes for mounting, Provider for the app-root context).\n// ---------------------------------------------------------------------------\n\nexport interface BlogWebsitePlugin {\n /** Standard plugin manifest (routes + metadata). */\n manifest: PluginManifest\n /** App-root context provider, pre-bound to this plugin's resolved provider/config. */\n Provider: FC<{ children: ReactNode }>\n /** The resolved data provider (exposed for advanced host wiring/tests). */\n dataProvider: BlogContextValue['provider']\n}\n\nexport function createBlogPlugin(options?: BlogPluginOptions): BlogWebsitePlugin {\n const config = resolveConfig(options)\n const provider =\n options?.dataProvider ??\n createSafeDataProvider(\n () => createSupabaseBlogProvider(),\n () => createMockBlogProvider({ seed: options?.seed, defaultAuthor: options?.defaultAuthor }),\n )\n\n const value: BlogContextValue = { provider, config }\n const Provider: FC<{ children: ReactNode }> = ({ children }) =>\n createElement(BlogProvider, { value, children })\n Provider.displayName = 'BlogPluginProvider'\n\n const manifest: PluginManifest = {\n id: 'blog',\n name: 'Blog',\n icon: 'Newspaper',\n version: '0.1.0',\n scope: options?.scope ?? 'universal',\n verticalId: options?.verticalId,\n scaffolds: ['website', 'landing_page'],\n defaultEnabled: true,\n dependencies: [],\n declaredFeatures: [{ id: 'blog', label: 'Blog', group: 'Attract' }],\n // Host owns website nav; no admin nav in M1.\n navigation: [],\n routes: [\n { path: config.basePath, component: BlogList, guard: 'public' },\n { path: `${config.basePath}/:slug`, component: PostDetail, guard: 'public' },\n ],\n widgets: [],\n }\n\n return { manifest, Provider, dataProvider: provider }\n}\n\n// Public API\nexport type { BlogPluginOptions, ResolvedBlogConfig } from './config'\nexport type { BlogPost, BlogListQuery, BlogAuthor } from './types'\nexport type { BlogDataProvider } from './data/types'\nexport { createMockBlogProvider, createSupabaseBlogProvider } from './data'\nexport type { BlogSeed, BlogSeedPost } from './data/mock'\nexport { BlogProvider, useBlogContext } from './context'\nexport type { BlogContextValue } from './context'\nexport { useBlogPosts, usePost } from './hooks'\nexport type { UseBlogPostsResult, UsePostResult } from './hooks'\nexport { BlogList } from './components/BlogList'\nexport { PostDetail } from './components/PostDetail'\nexport { PostCard } from './components/PostCard'\nexport { AuthorAvatar } from './components/AuthorAvatar'\n"]}
@@ -0,0 +1,29 @@
1
+ import { type FC, type ReactNode } from 'react';
2
+ import type { PluginManifest } from '@fayz-ai/core';
3
+ import { type BlogPluginOptions } from './config';
4
+ import { type BlogContextValue } from './context';
5
+ export interface BlogWebsitePlugin {
6
+ /** Standard plugin manifest (routes + metadata). */
7
+ manifest: PluginManifest;
8
+ /** App-root context provider, pre-bound to this plugin's resolved provider/config. */
9
+ Provider: FC<{
10
+ children: ReactNode;
11
+ }>;
12
+ /** The resolved data provider (exposed for advanced host wiring/tests). */
13
+ dataProvider: BlogContextValue['provider'];
14
+ }
15
+ export declare function createBlogPlugin(options?: BlogPluginOptions): BlogWebsitePlugin;
16
+ export type { BlogPluginOptions, ResolvedBlogConfig } from './config';
17
+ export type { BlogPost, BlogListQuery, BlogAuthor } from './types';
18
+ export type { BlogDataProvider } from './data/types';
19
+ export { createMockBlogProvider, createSupabaseBlogProvider } from './data';
20
+ export type { BlogSeed, BlogSeedPost } from './data/mock';
21
+ export { BlogProvider, useBlogContext } from './context';
22
+ export type { BlogContextValue } from './context';
23
+ export { useBlogPosts, usePost } from './hooks';
24
+ export type { UseBlogPostsResult, UsePostResult } from './hooks';
25
+ export { BlogList } from './components/BlogList';
26
+ export { PostDetail } from './components/PostDetail';
27
+ export { PostCard } from './components/PostCard';
28
+ export { AuthorAvatar } from './components/AuthorAvatar';
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,EAAE,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAEnD,OAAO,EAAiB,KAAK,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAGhE,OAAO,EAAgB,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAc/D,MAAM,WAAW,iBAAiB;IAChC,oDAAoD;IACpD,QAAQ,EAAE,cAAc,CAAA;IACxB,sFAAsF;IACtF,QAAQ,EAAE,EAAE,CAAC;QAAE,QAAQ,EAAE,SAAS,CAAA;KAAE,CAAC,CAAA;IACrC,2EAA2E;IAC3E,YAAY,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAA;CAC3C;AAED,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,iBAAiB,CAmC/E;AAGD,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AACrE,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAClE,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AACpD,OAAO,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,QAAQ,CAAA;AAC3E,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AACxD,YAAY,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAC/C,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,360 @@
1
+ import { createContext, useContext, useState, useEffect, createElement } from 'react';
2
+ import { useSeo, createSafeDataProvider, organizationJsonLd, absoluteUrl, breadcrumbJsonLd } from '@fayz-ai/core';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
+ import { Link, useParams } from 'react-router-dom';
5
+ import { Clock } from 'lucide-react';
6
+
7
+ // src/index.ts
8
+
9
+ // src/config.ts
10
+ function resolveConfig(options) {
11
+ return {
12
+ basePath: options?.basePath ?? "/blog",
13
+ siteName: options?.siteName ?? "",
14
+ labels: {
15
+ listTitle: options?.labels?.listTitle ?? "Blog",
16
+ listSubtitle: options?.labels?.listSubtitle ?? "",
17
+ backToList: options?.labels?.backToList ?? "\u2190 Voltar ao blog"
18
+ }
19
+ };
20
+ }
21
+
22
+ // src/data/mock.ts
23
+ function slugify(input) {
24
+ return input.normalize("NFD").replace(/[^\x00-\x7f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 80);
25
+ }
26
+ var FALLBACK_AUTHOR = { name: "Equipe" };
27
+ function normalize(post, defaultAuthor) {
28
+ return {
29
+ ...post,
30
+ slug: post.slug ?? slugify(post.title),
31
+ body: post.body ?? post.excerpt,
32
+ author: post.author ?? defaultAuthor
33
+ };
34
+ }
35
+ var FALLBACK = [
36
+ {
37
+ title: "Bem-vindo ao blog",
38
+ excerpt: "Este \xE9 um artigo de exemplo servido pelo plugin de blog do Fayz.",
39
+ tag: "Novidades",
40
+ readTime: "2 min",
41
+ date: "Hoje",
42
+ image: "https://images.unsplash.com/photo-1499750310107-5fef28a66643?w=800&q=80"
43
+ }
44
+ ];
45
+ function createMockBlogProvider(options) {
46
+ const defaultAuthor = options?.defaultAuthor ?? FALLBACK_AUTHOR;
47
+ const posts = (options?.seed?.posts ?? FALLBACK).map((p) => normalize(p, defaultAuthor));
48
+ return {
49
+ async listPosts(query) {
50
+ let result = posts;
51
+ if (query?.tag) result = result.filter((p) => p.tag === query.tag);
52
+ if (query?.limit != null) result = result.slice(0, query.limit);
53
+ return result;
54
+ },
55
+ async getPost(slug) {
56
+ return posts.find((p) => p.slug === slug) ?? null;
57
+ }
58
+ };
59
+ }
60
+
61
+ // src/data/supabase.ts
62
+ function createSupabaseBlogProvider() {
63
+ const notImplemented = () => {
64
+ throw new Error(
65
+ "[plugin-blog] Supabase provider not implemented yet \u2014 deferred to Phase 2. Run on the mock/seed provider (no Supabase client configured) for now."
66
+ );
67
+ };
68
+ return {
69
+ listPosts: (_query) => notImplemented(),
70
+ getPost: (_slug) => notImplemented()
71
+ };
72
+ }
73
+ var BlogContext = createContext(null);
74
+ function BlogProvider({ value, children }) {
75
+ return /* @__PURE__ */ jsx(BlogContext.Provider, { value, children });
76
+ }
77
+ function useBlogContext() {
78
+ const ctx = useContext(BlogContext);
79
+ if (!ctx) {
80
+ throw new Error("[plugin-blog] useBlogContext must be used within <BlogProvider>. Mount the plugin Provider at the app root.");
81
+ }
82
+ return ctx;
83
+ }
84
+ function useBlogPosts(query) {
85
+ const { provider } = useBlogContext();
86
+ const [posts, setPosts] = useState([]);
87
+ const [loading, setLoading] = useState(true);
88
+ const [error, setError] = useState(null);
89
+ const limit = query?.limit;
90
+ const tag = query?.tag;
91
+ useEffect(() => {
92
+ let active = true;
93
+ setLoading(true);
94
+ provider.listPosts({ limit, tag }).then((result) => {
95
+ if (!active) return;
96
+ setPosts(result);
97
+ setError(null);
98
+ }).catch((err) => {
99
+ if (!active) return;
100
+ setError(err instanceof Error ? err : new Error(String(err)));
101
+ }).finally(() => {
102
+ if (active) setLoading(false);
103
+ });
104
+ return () => {
105
+ active = false;
106
+ };
107
+ }, [provider, limit, tag]);
108
+ return { posts, loading, error };
109
+ }
110
+ function AuthorAvatar({ author, className = "h-8 w-8 text-xs" }) {
111
+ if (author.avatarUrl) {
112
+ return /* @__PURE__ */ jsx(
113
+ "img",
114
+ {
115
+ src: author.avatarUrl,
116
+ alt: author.name,
117
+ loading: "lazy",
118
+ className: `shrink-0 rounded-full object-cover ${className}`
119
+ }
120
+ );
121
+ }
122
+ return /* @__PURE__ */ jsx("span", { className: `flex shrink-0 items-center justify-center rounded-full bg-accent font-semibold text-primary ${className}`, children: author.name.charAt(0).toUpperCase() });
123
+ }
124
+ function PostCard({ post, basePath = "/blog" }) {
125
+ return /* @__PURE__ */ jsxs(
126
+ Link,
127
+ {
128
+ to: `${basePath}/${post.slug}`,
129
+ className: "group flex flex-col rounded-2xl border border-border bg-card overflow-hidden shadow-sm hover:shadow-lg hover:-translate-y-1 transition-all duration-300",
130
+ children: [
131
+ /* @__PURE__ */ jsx("div", { className: "overflow-hidden h-48", children: /* @__PURE__ */ jsx(
132
+ "img",
133
+ {
134
+ src: post.image,
135
+ alt: post.title,
136
+ loading: "lazy",
137
+ className: "h-full w-full object-cover transition-transform duration-500 group-hover:scale-105"
138
+ }
139
+ ) }),
140
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col flex-1 p-6", children: [
141
+ /* @__PURE__ */ jsx("span", { className: "inline-block rounded-full bg-accent px-3 py-0.5 text-xs font-semibold text-primary mb-3 w-fit", children: post.tag }),
142
+ /* @__PURE__ */ jsx("h3", { className: "font-heading text-lg font-semibold text-foreground leading-snug mb-3 group-hover:text-primary transition-colors duration-200", children: post.title }),
143
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm leading-relaxed flex-1 mb-4", children: post.excerpt }),
144
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 pt-4 border-t border-border", children: [
145
+ /* @__PURE__ */ jsx(AuthorAvatar, { author: post.author, className: "h-7 w-7 text-xs" }),
146
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
147
+ /* @__PURE__ */ jsx("p", { className: "truncate text-xs font-semibold text-foreground", children: post.author.name }),
148
+ /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
149
+ post.date,
150
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "\xB7" }),
151
+ /* @__PURE__ */ jsx(Clock, { className: "h-3 w-3" }),
152
+ post.readTime
153
+ ] })
154
+ ] })
155
+ ] })
156
+ ] })
157
+ ]
158
+ }
159
+ );
160
+ }
161
+ function postUrl(basePath, slug) {
162
+ return absoluteUrl(`${basePath}/${slug}`);
163
+ }
164
+ function articleJsonLd(post, opts) {
165
+ const url = postUrl(opts.basePath, post.slug);
166
+ const posting = {
167
+ "@context": "https://schema.org",
168
+ "@type": "BlogPosting",
169
+ headline: post.title,
170
+ description: post.excerpt,
171
+ image: post.image ? [post.image] : void 0,
172
+ articleSection: post.tag,
173
+ mainEntityOfPage: { "@type": "WebPage", "@id": url },
174
+ author: {
175
+ "@type": "Person",
176
+ name: post.author.name,
177
+ ...post.author.role ? { jobTitle: post.author.role } : {}
178
+ },
179
+ ...post.publishedAt ? { datePublished: post.publishedAt, dateModified: post.publishedAt } : {},
180
+ ...opts.siteName ? { publisher: organizationJsonLd(opts.siteName) } : {}
181
+ };
182
+ const breadcrumb = breadcrumbJsonLd([
183
+ { name: "Blog", url: absoluteUrl(opts.basePath) },
184
+ { name: post.title, url }
185
+ ]);
186
+ return [posting, breadcrumb];
187
+ }
188
+ function blogJsonLd(posts, opts) {
189
+ return [
190
+ {
191
+ "@context": "https://schema.org",
192
+ "@type": "Blog",
193
+ name: opts.title,
194
+ url: absoluteUrl(opts.basePath),
195
+ ...opts.siteName ? { publisher: organizationJsonLd(opts.siteName) } : {},
196
+ blogPost: posts.map((p) => ({
197
+ "@type": "BlogPosting",
198
+ headline: p.title,
199
+ url: postUrl(opts.basePath, p.slug),
200
+ image: p.image,
201
+ author: { "@type": "Person", name: p.author.name },
202
+ ...p.publishedAt ? { datePublished: p.publishedAt } : {}
203
+ }))
204
+ }
205
+ ];
206
+ }
207
+ function BlogList() {
208
+ const { config } = useBlogContext();
209
+ const { posts, loading, error } = useBlogPosts();
210
+ const listTitle = config.labels.listSubtitle || config.labels.listTitle;
211
+ useSeo({
212
+ title: `${listTitle}${config.siteName ? ` \xB7 ${config.siteName}` : ""}`,
213
+ description: config.labels.listSubtitle || `Artigos do blog${config.siteName ? ` da ${config.siteName}` : ""}.`,
214
+ canonical: `${typeof window !== "undefined" ? window.location.origin : ""}${config.basePath}`,
215
+ type: "website",
216
+ siteName: config.siteName,
217
+ jsonLd: posts.length ? blogJsonLd(posts, { basePath: config.basePath, siteName: config.siteName, title: listTitle }) : void 0
218
+ });
219
+ return /* @__PURE__ */ jsx("section", { className: "py-20 bg-background", children: /* @__PURE__ */ jsxs("div", { className: "container mx-auto px-6", children: [
220
+ /* @__PURE__ */ jsxs("div", { className: "mb-12 max-w-2xl", children: [
221
+ /* @__PURE__ */ jsx("span", { className: "inline-block rounded-full bg-accent px-4 py-1 text-xs font-semibold uppercase tracking-widest text-primary mb-4", children: config.labels.listTitle }),
222
+ config.labels.listSubtitle ? /* @__PURE__ */ jsx("h1", { className: "font-heading text-4xl md:text-5xl font-bold text-foreground", children: config.labels.listSubtitle }) : null
223
+ ] }),
224
+ loading ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "Carregando artigos\u2026" }) : error ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "N\xE3o foi poss\xEDvel carregar os artigos." }) : posts.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "Nenhum artigo publicado ainda." }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-6", children: posts.map((post) => /* @__PURE__ */ jsx(PostCard, { post, basePath: config.basePath }, post.slug)) })
225
+ ] }) });
226
+ }
227
+ function usePost(slug) {
228
+ const { provider } = useBlogContext();
229
+ const [post, setPost] = useState(null);
230
+ const [loading, setLoading] = useState(true);
231
+ const [error, setError] = useState(null);
232
+ useEffect(() => {
233
+ let active = true;
234
+ if (!slug) {
235
+ setPost(null);
236
+ setLoading(false);
237
+ return;
238
+ }
239
+ setLoading(true);
240
+ provider.getPost(slug).then((result) => {
241
+ if (!active) return;
242
+ setPost(result);
243
+ setError(null);
244
+ }).catch((err) => {
245
+ if (!active) return;
246
+ setError(err instanceof Error ? err : new Error(String(err)));
247
+ }).finally(() => {
248
+ if (active) setLoading(false);
249
+ });
250
+ return () => {
251
+ active = false;
252
+ };
253
+ }, [provider, slug]);
254
+ return { post, loading, error };
255
+ }
256
+ function PostDetail() {
257
+ const { config } = useBlogContext();
258
+ const { slug } = useParams();
259
+ const { post, loading, error } = usePost(slug);
260
+ useSeo(
261
+ post ? {
262
+ title: `${post.title}${config.siteName ? ` \xB7 ${config.siteName}` : ""}`,
263
+ description: post.excerpt,
264
+ canonical: postUrl(config.basePath, post.slug),
265
+ image: post.image,
266
+ type: "article",
267
+ siteName: config.siteName,
268
+ jsonLd: articleJsonLd(post, { basePath: config.basePath, siteName: config.siteName })
269
+ } : { title: config.siteName || "Blog" }
270
+ );
271
+ const paragraphs = post?.body ? post.body.split(/\n\s*\n/).map((p) => p.trim()).filter(Boolean) : [];
272
+ return /* @__PURE__ */ jsx("article", { className: "py-16 bg-background", children: /* @__PURE__ */ jsxs("div", { className: "container mx-auto px-6 max-w-3xl", children: [
273
+ /* @__PURE__ */ jsx(Link, { to: config.basePath, className: "text-sm font-medium text-primary hover:underline", children: config.labels.backToList }),
274
+ loading ? /* @__PURE__ */ jsx("p", { className: "mt-10 text-muted-foreground", children: "Carregando\u2026" }) : error || !post ? /* @__PURE__ */ jsxs("div", { className: "mt-10", children: [
275
+ /* @__PURE__ */ jsx("h1", { className: "font-heading text-3xl font-bold text-foreground", children: "Artigo n\xE3o encontrado" }),
276
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-muted-foreground", children: "Este artigo pode ter sido movido ou removido." })
277
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
278
+ /* @__PURE__ */ jsx("h1", { className: "mt-8 font-heading text-4xl md:text-5xl font-bold text-foreground leading-tight", children: post.title }),
279
+ /* @__PURE__ */ jsxs("div", { className: "mt-6 flex items-center gap-3", children: [
280
+ /* @__PURE__ */ jsx(AuthorAvatar, { author: post.author, className: "h-11 w-11 text-base" }),
281
+ /* @__PURE__ */ jsxs("div", { children: [
282
+ /* @__PURE__ */ jsx("p", { className: "font-semibold text-foreground", children: post.author.name }),
283
+ /* @__PURE__ */ jsxs("p", { className: "flex flex-wrap items-center gap-1.5 text-sm text-muted-foreground", children: [
284
+ post.author.role ? /* @__PURE__ */ jsxs("span", { children: [
285
+ post.author.role,
286
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: " \xB7 " })
287
+ ] }) : null,
288
+ /* @__PURE__ */ jsx("time", { dateTime: post.publishedAt, children: post.date }),
289
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
290
+ /* @__PURE__ */ jsx(Clock, { className: "h-3 w-3" }),
291
+ post.readTime,
292
+ " de leitura"
293
+ ] })
294
+ ] })
295
+ ] })
296
+ ] }),
297
+ /* @__PURE__ */ jsx("div", { className: "mt-8 overflow-hidden rounded-2xl", children: /* @__PURE__ */ jsx("img", { src: post.image, alt: post.title, className: "w-full object-cover", loading: "lazy" }) }),
298
+ /* @__PURE__ */ jsxs("div", { className: "mt-8 space-y-5", children: [
299
+ /* @__PURE__ */ jsx("p", { className: "text-lg text-foreground/90 leading-relaxed font-medium", children: post.excerpt }),
300
+ paragraphs.map((para, i) => /* @__PURE__ */ jsx("p", { className: "text-muted-foreground leading-relaxed", children: para }, i))
301
+ ] }),
302
+ /* @__PURE__ */ jsxs("div", { className: "mt-10 flex flex-wrap items-center gap-2 border-t border-border pt-6", children: [
303
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Categoria:" }),
304
+ /* @__PURE__ */ jsx(
305
+ Link,
306
+ {
307
+ to: config.basePath,
308
+ className: "rounded-full bg-accent px-3 py-1 text-xs font-semibold text-primary transition-colors hover:bg-accent/70",
309
+ children: post.tag
310
+ }
311
+ )
312
+ ] }),
313
+ post.author.bio ? /* @__PURE__ */ jsxs("div", { className: "mt-8 flex items-start gap-4 rounded-2xl border border-border bg-muted/40 p-6", children: [
314
+ /* @__PURE__ */ jsx(AuthorAvatar, { author: post.author, className: "h-14 w-14 text-lg" }),
315
+ /* @__PURE__ */ jsxs("div", { children: [
316
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Escrito por" }),
317
+ /* @__PURE__ */ jsx("p", { className: "font-heading text-lg font-bold text-foreground", children: post.author.name }),
318
+ post.author.role ? /* @__PURE__ */ jsx("p", { className: "text-sm text-primary", children: post.author.role }) : null,
319
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground leading-relaxed", children: post.author.bio })
320
+ ] })
321
+ ] }) : null
322
+ ] })
323
+ ] }) });
324
+ }
325
+
326
+ // src/index.ts
327
+ function createBlogPlugin(options) {
328
+ const config = resolveConfig(options);
329
+ const provider = options?.dataProvider ?? createSafeDataProvider(
330
+ () => createSupabaseBlogProvider(),
331
+ () => createMockBlogProvider({ seed: options?.seed, defaultAuthor: options?.defaultAuthor })
332
+ );
333
+ const value = { provider, config };
334
+ const Provider = ({ children }) => createElement(BlogProvider, { value, children });
335
+ Provider.displayName = "BlogPluginProvider";
336
+ const manifest = {
337
+ id: "blog",
338
+ name: "Blog",
339
+ icon: "Newspaper",
340
+ version: "0.1.0",
341
+ scope: options?.scope ?? "universal",
342
+ verticalId: options?.verticalId,
343
+ scaffolds: ["website", "landing_page"],
344
+ defaultEnabled: true,
345
+ dependencies: [],
346
+ declaredFeatures: [{ id: "blog", label: "Blog", group: "Attract" }],
347
+ // Host owns website nav; no admin nav in M1.
348
+ navigation: [],
349
+ routes: [
350
+ { path: config.basePath, component: BlogList, guard: "public" },
351
+ { path: `${config.basePath}/:slug`, component: PostDetail, guard: "public" }
352
+ ],
353
+ widgets: []
354
+ };
355
+ return { manifest, Provider, dataProvider: provider };
356
+ }
357
+
358
+ export { AuthorAvatar, BlogList, BlogProvider, PostCard, PostDetail, createBlogPlugin, createMockBlogProvider, createSupabaseBlogProvider, useBlogContext, useBlogPosts, usePost };
359
+ //# sourceMappingURL=index.js.map
360
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts","../src/data/mock.ts","../src/data/supabase.ts","../src/context.tsx","../src/hooks/useBlogPosts.ts","../src/components/AuthorAvatar.tsx","../src/components/PostCard.tsx","../src/seo.ts","../src/components/BlogList.tsx","../src/hooks/usePost.ts","../src/components/PostDetail.tsx","../src/index.ts"],"names":["jsx","jsxs","useState","useEffect","Link","Clock"],"mappings":"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","file":"index.js","sourcesContent":["import type { PluginScope, VerticalId } from '@fayz-ai/core'\nimport type { BlogDataProvider } from './data/types'\nimport type { BlogSeed } from './data/mock'\nimport type { BlogAuthor } from './types'\n\nexport interface BlogPluginOptions {\n /** Base path the plugin mounts its public routes under. Default '/blog'. */\n basePath?: string\n /** Seed posts for the mock provider (used until a real backend is wired). */\n seed?: BlogSeed\n /** Default byline author, applied to any post that doesn't specify its own. */\n defaultAuthor?: BlogAuthor\n /** Publisher/brand name — used for SEO (og:site_name, JSON-LD publisher). */\n siteName?: string\n /** Inject a custom provider (e.g. a CMS). Overrides the safe mock/Supabase resolver. */\n dataProvider?: BlogDataProvider\n /** Section eyebrow / labels for the default list screen. */\n labels?: {\n /** Title shown at the top of the /blog list screen. */\n listTitle?: string\n /** Subtitle under the list title. */\n listSubtitle?: string\n /** \"Back to blog\" link label on the detail screen. */\n backToList?: string\n }\n scope?: PluginScope\n verticalId?: VerticalId\n}\n\nexport interface ResolvedBlogConfig {\n basePath: string\n siteName: string\n labels: {\n listTitle: string\n listSubtitle: string\n backToList: string\n }\n}\n\nexport function resolveConfig(options?: BlogPluginOptions): ResolvedBlogConfig {\n return {\n basePath: options?.basePath ?? '/blog',\n siteName: options?.siteName ?? '',\n labels: {\n listTitle: options?.labels?.listTitle ?? 'Blog',\n listSubtitle: options?.labels?.listSubtitle ?? '',\n backToList: options?.labels?.backToList ?? '← Voltar ao blog',\n },\n }\n}\n","import type { BlogDataProvider } from './types'\nimport type { BlogPost, BlogListQuery, BlogAuthor } from '../types'\n\n/** Slugify a title when a seed post omits an explicit slug. */\nfunction slugify(input: string): string {\n return input\n .normalize('NFD')\n // Drop combining accent marks (and any other non-ASCII) so \"saúde\" -> \"saude\".\n .replace(/[^\\x00-\\x7f]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/(^-|-$)/g, '')\n .slice(0, 80)\n}\n\n/** Seed post — same shape as BlogPost but slug/body/author may be derived. */\nexport type BlogSeedPost = Omit<BlogPost, 'slug' | 'body' | 'author'> & {\n slug?: string\n body?: string\n author?: BlogAuthor\n}\n\nexport interface BlogSeed {\n posts: BlogSeedPost[]\n}\n\nexport interface MockBlogProviderOptions {\n seed?: BlogSeed\n /** Fallback byline used for posts without their own author. */\n defaultAuthor?: BlogAuthor\n}\n\nconst FALLBACK_AUTHOR: BlogAuthor = { name: 'Equipe' }\n\nfunction normalize(post: BlogSeedPost, defaultAuthor: BlogAuthor): BlogPost {\n return {\n ...post,\n slug: post.slug ?? slugify(post.title),\n body: post.body ?? post.excerpt,\n author: post.author ?? defaultAuthor,\n }\n}\n\n/** A built-in fallback post so an unseeded plugin still renders something. */\nconst FALLBACK: BlogSeedPost[] = [\n {\n title: 'Bem-vindo ao blog',\n excerpt: 'Este é um artigo de exemplo servido pelo plugin de blog do Fayz.',\n tag: 'Novidades',\n readTime: '2 min',\n date: 'Hoje',\n image: 'https://images.unsplash.com/photo-1499750310107-5fef28a66643?w=800&q=80',\n },\n]\n\nexport function createMockBlogProvider(options?: MockBlogProviderOptions): BlogDataProvider {\n const defaultAuthor = options?.defaultAuthor ?? FALLBACK_AUTHOR\n const posts: BlogPost[] = (options?.seed?.posts ?? FALLBACK).map((p) => normalize(p, defaultAuthor))\n\n return {\n async listPosts(query?: BlogListQuery): Promise<BlogPost[]> {\n let result = posts\n if (query?.tag) result = result.filter((p) => p.tag === query.tag)\n if (query?.limit != null) result = result.slice(0, query.limit)\n return result\n },\n async getPost(slug: string): Promise<BlogPost | null> {\n return posts.find((p) => p.slug === slug) ?? null\n },\n }\n}\n","import type { BlogDataProvider } from './types'\nimport type { BlogPost, BlogListQuery } from '../types'\n\n// ---------------------------------------------------------------------------\n// Supabase-backed blog provider — STUB.\n//\n// Deferred (Phase 2): read from a `blog_posts` table (tenant-scoped, RLS) with\n// columns mirroring BlogPost (slug, title, excerpt, body, tag, read_time, date,\n// image, author). Wiring this up is a pure provider swap — no hook/component\n// change — because everything downstream depends only on BlogDataProvider.\n//\n// Until then this throws, which makes createSafeDataProvider fall back to the\n// mock provider whenever no Supabase client is configured. If a client IS\n// configured but this is still a stub, surface a clear error.\n// ---------------------------------------------------------------------------\n\nexport function createSupabaseBlogProvider(): BlogDataProvider {\n const notImplemented = (): never => {\n throw new Error(\n '[plugin-blog] Supabase provider not implemented yet — deferred to Phase 2. ' +\n 'Run on the mock/seed provider (no Supabase client configured) for now.',\n )\n }\n return {\n listPosts: (_query?: BlogListQuery): Promise<BlogPost[]> => notImplemented(),\n getPost: (_slug: string): Promise<BlogPost | null> => notImplemented(),\n }\n}\n","import { createContext, useContext, type ReactNode } from 'react'\nimport type { BlogDataProvider } from './data/types'\nimport type { ResolvedBlogConfig } from './config'\n\nexport interface BlogContextValue {\n provider: BlogDataProvider\n config: ResolvedBlogConfig\n}\n\nconst BlogContext = createContext<BlogContextValue | null>(null)\n\nexport function BlogProvider({ value, children }: { value: BlogContextValue; children: ReactNode }) {\n return <BlogContext.Provider value={value}>{children}</BlogContext.Provider>\n}\n\n/** Read the blog provider + config. Throws if used outside a BlogProvider. */\nexport function useBlogContext(): BlogContextValue {\n const ctx = useContext(BlogContext)\n if (!ctx) {\n throw new Error('[plugin-blog] useBlogContext must be used within <BlogProvider>. Mount the plugin Provider at the app root.')\n }\n return ctx\n}\n","import { useEffect, useState } from 'react'\nimport { useBlogContext } from '../context'\nimport type { BlogPost, BlogListQuery } from '../types'\n\nexport interface UseBlogPostsResult {\n posts: BlogPost[]\n loading: boolean\n error: Error | null\n}\n\n/**\n * Fetch the post list from the active blog provider. The host maps `posts` into\n * its own card markup; the plugin never dictates presentation.\n */\nexport function useBlogPosts(query?: BlogListQuery): UseBlogPostsResult {\n const { provider } = useBlogContext()\n const [posts, setPosts] = useState<BlogPost[]>([])\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n const limit = query?.limit\n const tag = query?.tag\n\n useEffect(() => {\n let active = true\n setLoading(true)\n provider\n .listPosts({ limit, tag })\n .then((result) => {\n if (!active) return\n setPosts(result)\n setError(null)\n })\n .catch((err) => {\n if (!active) return\n setError(err instanceof Error ? err : new Error(String(err)))\n })\n .finally(() => {\n if (active) setLoading(false)\n })\n return () => {\n active = false\n }\n }, [provider, limit, tag])\n\n return { posts, loading, error }\n}\n","import type { BlogAuthor } from '../types'\n\n/** Round author avatar with a name-initial fallback (token-only). */\nexport function AuthorAvatar({ author, className = 'h-8 w-8 text-xs' }: { author: BlogAuthor; className?: string }) {\n if (author.avatarUrl) {\n return (\n <img\n src={author.avatarUrl}\n alt={author.name}\n loading=\"lazy\"\n className={`shrink-0 rounded-full object-cover ${className}`}\n />\n )\n }\n return (\n <span className={`flex shrink-0 items-center justify-center rounded-full bg-accent font-semibold text-primary ${className}`}>\n {author.name.charAt(0).toUpperCase()}\n </span>\n )\n}\n","import { Link } from 'react-router-dom'\nimport { Clock } from 'lucide-react'\nimport type { BlogPost } from '../types'\nimport { AuthorAvatar } from './AuthorAvatar'\n\n/**\n * Default post card — token-only so it inherits the host theme. Optional: a host\n * with bespoke card markup can ignore this and map `useBlogPosts()` itself.\n */\nexport function PostCard({ post, basePath = '/blog' }: { post: BlogPost; basePath?: string }) {\n return (\n <Link\n to={`${basePath}/${post.slug}`}\n className=\"group flex flex-col rounded-2xl border border-border bg-card overflow-hidden shadow-sm hover:shadow-lg hover:-translate-y-1 transition-all duration-300\"\n >\n <div className=\"overflow-hidden h-48\">\n <img\n src={post.image}\n alt={post.title}\n loading=\"lazy\"\n className=\"h-full w-full object-cover transition-transform duration-500 group-hover:scale-105\"\n />\n </div>\n <div className=\"flex flex-col flex-1 p-6\">\n <span className=\"inline-block rounded-full bg-accent px-3 py-0.5 text-xs font-semibold text-primary mb-3 w-fit\">\n {post.tag}\n </span>\n <h3 className=\"font-heading text-lg font-semibold text-foreground leading-snug mb-3 group-hover:text-primary transition-colors duration-200\">\n {post.title}\n </h3>\n <p className=\"text-muted-foreground text-sm leading-relaxed flex-1 mb-4\">{post.excerpt}</p>\n <div className=\"flex items-center gap-2.5 pt-4 border-t border-border\">\n <AuthorAvatar author={post.author} className=\"h-7 w-7 text-xs\" />\n <div className=\"min-w-0\">\n <p className=\"truncate text-xs font-semibold text-foreground\">{post.author.name}</p>\n <p className=\"flex items-center gap-1 text-[11px] text-muted-foreground\">\n {post.date}<span aria-hidden>·</span><Clock className=\"h-3 w-3\" />{post.readTime}\n </p>\n </div>\n </div>\n </div>\n </Link>\n )\n}\n","import { absoluteUrl, breadcrumbJsonLd, organizationJsonLd } from '@fayz-ai/core'\nimport type { BlogPost } from './types'\n\n// ---------------------------------------------------------------------------\n// Blog-specific schema.org builders. The head-manager mechanics (useSeo /\n// applySeo / meta+OG+Twitter) live in @fayz-ai/core — this only owns the\n// domain shapes (BlogPosting, Blog) that plug into core's SeoConfig.jsonLd.\n// ---------------------------------------------------------------------------\n\n/** Absolute canonical URL for a post. */\nexport function postUrl(basePath: string, slug: string): string {\n return absoluteUrl(`${basePath}/${slug}`)\n}\n\n/** schema.org BlogPosting + BreadcrumbList for an article page. */\nexport function articleJsonLd(post: BlogPost, opts: { basePath: string; siteName: string }): Array<Record<string, unknown>> {\n const url = postUrl(opts.basePath, post.slug)\n const posting: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': 'BlogPosting',\n headline: post.title,\n description: post.excerpt,\n image: post.image ? [post.image] : undefined,\n articleSection: post.tag,\n mainEntityOfPage: { '@type': 'WebPage', '@id': url },\n author: {\n '@type': 'Person',\n name: post.author.name,\n ...(post.author.role ? { jobTitle: post.author.role } : {}),\n },\n ...(post.publishedAt ? { datePublished: post.publishedAt, dateModified: post.publishedAt } : {}),\n ...(opts.siteName ? { publisher: organizationJsonLd(opts.siteName) } : {}),\n }\n const breadcrumb = breadcrumbJsonLd([\n { name: 'Blog', url: absoluteUrl(opts.basePath) },\n { name: post.title, url },\n ])\n return [posting, breadcrumb]\n}\n\n/** schema.org Blog + post list for the listing page. */\nexport function blogJsonLd(posts: BlogPost[], opts: { basePath: string; siteName: string; title: string }): Array<Record<string, unknown>> {\n return [\n {\n '@context': 'https://schema.org',\n '@type': 'Blog',\n name: opts.title,\n url: absoluteUrl(opts.basePath),\n ...(opts.siteName ? { publisher: organizationJsonLd(opts.siteName) } : {}),\n blogPost: posts.map((p) => ({\n '@type': 'BlogPosting',\n headline: p.title,\n url: postUrl(opts.basePath, p.slug),\n image: p.image,\n author: { '@type': 'Person', name: p.author.name },\n ...(p.publishedAt ? { datePublished: p.publishedAt } : {}),\n })),\n },\n ]\n}\n\n// Re-export the generic hook so blog components import SEO from one place.\nexport { useSeo } from '@fayz-ai/core'\n","import { useBlogPosts } from '../hooks/useBlogPosts'\nimport { useBlogContext } from '../context'\nimport { PostCard } from './PostCard'\nimport { useSeo, blogJsonLd } from '../seo'\n\n/**\n * Full /blog list screen. Rendered inside the host's chrome (Header/Footer),\n * so it only owns the section body — token-only for theme inheritance.\n */\nexport function BlogList() {\n const { config } = useBlogContext()\n const { posts, loading, error } = useBlogPosts()\n\n const listTitle = config.labels.listSubtitle || config.labels.listTitle\n useSeo({\n title: `${listTitle}${config.siteName ? ` · ${config.siteName}` : ''}`,\n description: config.labels.listSubtitle || `Artigos do blog${config.siteName ? ` da ${config.siteName}` : ''}.`,\n canonical: `${typeof window !== 'undefined' ? window.location.origin : ''}${config.basePath}`,\n type: 'website',\n siteName: config.siteName,\n jsonLd: posts.length ? blogJsonLd(posts, { basePath: config.basePath, siteName: config.siteName, title: listTitle }) : undefined,\n })\n\n return (\n <section className=\"py-20 bg-background\">\n <div className=\"container mx-auto px-6\">\n <div className=\"mb-12 max-w-2xl\">\n <span className=\"inline-block rounded-full bg-accent px-4 py-1 text-xs font-semibold uppercase tracking-widest text-primary mb-4\">\n {config.labels.listTitle}\n </span>\n {config.labels.listSubtitle ? (\n <h1 className=\"font-heading text-4xl md:text-5xl font-bold text-foreground\">\n {config.labels.listSubtitle}\n </h1>\n ) : null}\n </div>\n\n {loading ? (\n <p className=\"text-muted-foreground\">Carregando artigos…</p>\n ) : error ? (\n <p className=\"text-muted-foreground\">Não foi possível carregar os artigos.</p>\n ) : posts.length === 0 ? (\n <p className=\"text-muted-foreground\">Nenhum artigo publicado ainda.</p>\n ) : (\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-6\">\n {posts.map((post) => (\n <PostCard key={post.slug} post={post} basePath={config.basePath} />\n ))}\n </div>\n )}\n </div>\n </section>\n )\n}\n","import { useEffect, useState } from 'react'\nimport { useBlogContext } from '../context'\nimport type { BlogPost } from '../types'\n\nexport interface UsePostResult {\n post: BlogPost | null\n loading: boolean\n error: Error | null\n}\n\n/** Fetch a single post by slug from the active blog provider. */\nexport function usePost(slug: string | undefined): UsePostResult {\n const { provider } = useBlogContext()\n const [post, setPost] = useState<BlogPost | null>(null)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n useEffect(() => {\n let active = true\n if (!slug) {\n setPost(null)\n setLoading(false)\n return\n }\n setLoading(true)\n provider\n .getPost(slug)\n .then((result) => {\n if (!active) return\n setPost(result)\n setError(null)\n })\n .catch((err) => {\n if (!active) return\n setError(err instanceof Error ? err : new Error(String(err)))\n })\n .finally(() => {\n if (active) setLoading(false)\n })\n return () => {\n active = false\n }\n }, [provider, slug])\n\n return { post, loading, error }\n}\n","import { Link, useParams } from 'react-router-dom'\nimport { Clock } from 'lucide-react'\nimport { usePost } from '../hooks/usePost'\nimport { useBlogContext } from '../context'\nimport { AuthorAvatar } from './AuthorAvatar'\nimport { useSeo, articleJsonLd, postUrl } from '../seo'\n\n/**\n * /blog/:slug detail screen. Reads the slug from the router and renders the\n * article inside the host chrome. Token-only for theme inheritance. Sets rich\n * SEO/JSON-LD per article (BlogPosting + BreadcrumbList).\n */\nexport function PostDetail() {\n const { config } = useBlogContext()\n const { slug } = useParams<{ slug: string }>()\n const { post, loading, error } = usePost(slug)\n\n useSeo(\n post\n ? {\n title: `${post.title}${config.siteName ? ` · ${config.siteName}` : ''}`,\n description: post.excerpt,\n canonical: postUrl(config.basePath, post.slug),\n image: post.image,\n type: 'article',\n siteName: config.siteName,\n jsonLd: articleJsonLd(post, { basePath: config.basePath, siteName: config.siteName }),\n }\n : { title: config.siteName || 'Blog' },\n )\n\n const paragraphs = post?.body ? post.body.split(/\\n\\s*\\n/).map((p) => p.trim()).filter(Boolean) : []\n\n return (\n <article className=\"py-16 bg-background\">\n <div className=\"container mx-auto px-6 max-w-3xl\">\n <Link to={config.basePath} className=\"text-sm font-medium text-primary hover:underline\">\n {config.labels.backToList}\n </Link>\n\n {loading ? (\n <p className=\"mt-10 text-muted-foreground\">Carregando…</p>\n ) : error || !post ? (\n <div className=\"mt-10\">\n <h1 className=\"font-heading text-3xl font-bold text-foreground\">Artigo não encontrado</h1>\n <p className=\"mt-2 text-muted-foreground\">Este artigo pode ter sido movido ou removido.</p>\n </div>\n ) : (\n <>\n <h1 className=\"mt-8 font-heading text-4xl md:text-5xl font-bold text-foreground leading-tight\">\n {post.title}\n </h1>\n\n {/* Medium-style byline */}\n <div className=\"mt-6 flex items-center gap-3\">\n <AuthorAvatar author={post.author} className=\"h-11 w-11 text-base\" />\n <div>\n <p className=\"font-semibold text-foreground\">{post.author.name}</p>\n <p className=\"flex flex-wrap items-center gap-1.5 text-sm text-muted-foreground\">\n {post.author.role ? <span>{post.author.role}<span aria-hidden> · </span></span> : null}\n <time dateTime={post.publishedAt}>{post.date}</time>\n <span className=\"flex items-center gap-1\"><Clock className=\"h-3 w-3\" />{post.readTime} de leitura</span>\n </p>\n </div>\n </div>\n\n <div className=\"mt-8 overflow-hidden rounded-2xl\">\n <img src={post.image} alt={post.title} className=\"w-full object-cover\" loading=\"lazy\" />\n </div>\n\n <div className=\"mt-8 space-y-5\">\n <p className=\"text-lg text-foreground/90 leading-relaxed font-medium\">{post.excerpt}</p>\n {paragraphs.map((para, i) => (\n <p key={i} className=\"text-muted-foreground leading-relaxed\">\n {para}\n </p>\n ))}\n </div>\n\n {/* Category tag — at the bottom, Medium-style */}\n <div className=\"mt-10 flex flex-wrap items-center gap-2 border-t border-border pt-6\">\n <span className=\"text-sm text-muted-foreground\">Categoria:</span>\n <Link\n to={config.basePath}\n className=\"rounded-full bg-accent px-3 py-1 text-xs font-semibold text-primary transition-colors hover:bg-accent/70\"\n >\n {post.tag}\n </Link>\n </div>\n\n {/* About the author */}\n {post.author.bio ? (\n <div className=\"mt-8 flex items-start gap-4 rounded-2xl border border-border bg-muted/40 p-6\">\n <AuthorAvatar author={post.author} className=\"h-14 w-14 text-lg\" />\n <div>\n <p className=\"text-xs font-semibold uppercase tracking-wide text-muted-foreground\">Escrito por</p>\n <p className=\"font-heading text-lg font-bold text-foreground\">{post.author.name}</p>\n {post.author.role ? <p className=\"text-sm text-primary\">{post.author.role}</p> : null}\n <p className=\"mt-2 text-sm text-muted-foreground leading-relaxed\">{post.author.bio}</p>\n </div>\n </div>\n ) : null}\n </>\n )}\n </div>\n </article>\n )\n}\n","import { createElement, type FC, type ReactNode } from 'react'\nimport type { PluginManifest } from '@fayz-ai/core'\nimport { createSafeDataProvider } from '@fayz-ai/core'\nimport { resolveConfig, type BlogPluginOptions } from './config'\nimport { createMockBlogProvider } from './data/mock'\nimport { createSupabaseBlogProvider } from './data/supabase'\nimport { BlogProvider, type BlogContextValue } from './context'\nimport { BlogList } from './components/BlogList'\nimport { PostDetail } from './components/PostDetail'\n\n// ---------------------------------------------------------------------------\n// @fayz-ai/plugin-blog — website-surface blog/articles plugin.\n//\n// First real consumer of scaffolds:['website']. Ships public routes (/blog,\n// /blog/:slug) + data hooks (useBlogPosts/usePost). A bespoke host site feeds\n// its own card markup from the hooks and mounts the routes inside its chrome.\n// See createBlogPlugin's return: a { manifest, Provider } bundle the host reads\n// uniformly (manifest.routes for mounting, Provider for the app-root context).\n// ---------------------------------------------------------------------------\n\nexport interface BlogWebsitePlugin {\n /** Standard plugin manifest (routes + metadata). */\n manifest: PluginManifest\n /** App-root context provider, pre-bound to this plugin's resolved provider/config. */\n Provider: FC<{ children: ReactNode }>\n /** The resolved data provider (exposed for advanced host wiring/tests). */\n dataProvider: BlogContextValue['provider']\n}\n\nexport function createBlogPlugin(options?: BlogPluginOptions): BlogWebsitePlugin {\n const config = resolveConfig(options)\n const provider =\n options?.dataProvider ??\n createSafeDataProvider(\n () => createSupabaseBlogProvider(),\n () => createMockBlogProvider({ seed: options?.seed, defaultAuthor: options?.defaultAuthor }),\n )\n\n const value: BlogContextValue = { provider, config }\n const Provider: FC<{ children: ReactNode }> = ({ children }) =>\n createElement(BlogProvider, { value, children })\n Provider.displayName = 'BlogPluginProvider'\n\n const manifest: PluginManifest = {\n id: 'blog',\n name: 'Blog',\n icon: 'Newspaper',\n version: '0.1.0',\n scope: options?.scope ?? 'universal',\n verticalId: options?.verticalId,\n scaffolds: ['website', 'landing_page'],\n defaultEnabled: true,\n dependencies: [],\n declaredFeatures: [{ id: 'blog', label: 'Blog', group: 'Attract' }],\n // Host owns website nav; no admin nav in M1.\n navigation: [],\n routes: [\n { path: config.basePath, component: BlogList, guard: 'public' },\n { path: `${config.basePath}/:slug`, component: PostDetail, guard: 'public' },\n ],\n widgets: [],\n }\n\n return { manifest, Provider, dataProvider: provider }\n}\n\n// Public API\nexport type { BlogPluginOptions, ResolvedBlogConfig } from './config'\nexport type { BlogPost, BlogListQuery, BlogAuthor } from './types'\nexport type { BlogDataProvider } from './data/types'\nexport { createMockBlogProvider, createSupabaseBlogProvider } from './data'\nexport type { BlogSeed, BlogSeedPost } from './data/mock'\nexport { BlogProvider, useBlogContext } from './context'\nexport type { BlogContextValue } from './context'\nexport { useBlogPosts, usePost } from './hooks'\nexport type { UseBlogPostsResult, UsePostResult } from './hooks'\nexport { BlogList } from './components/BlogList'\nexport { PostDetail } from './components/PostDetail'\nexport { PostCard } from './components/PostCard'\nexport { AuthorAvatar } from './components/AuthorAvatar'\n"]}
package/dist/seo.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ import type { BlogPost } from './types';
2
+ /** Absolute canonical URL for a post. */
3
+ export declare function postUrl(basePath: string, slug: string): string;
4
+ /** schema.org BlogPosting + BreadcrumbList for an article page. */
5
+ export declare function articleJsonLd(post: BlogPost, opts: {
6
+ basePath: string;
7
+ siteName: string;
8
+ }): Array<Record<string, unknown>>;
9
+ /** schema.org Blog + post list for the listing page. */
10
+ export declare function blogJsonLd(posts: BlogPost[], opts: {
11
+ basePath: string;
12
+ siteName: string;
13
+ title: string;
14
+ }): Array<Record<string, unknown>>;
15
+ export { useSeo } from '@fayz-ai/core';
16
+ //# sourceMappingURL=seo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seo.d.ts","sourceRoot":"","sources":["../src/seo.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAQvC,yCAAyC;AACzC,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,mEAAmE;AACnE,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAuB1H;AAED,wDAAwD;AACxD,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAkBzI;AAGD,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA"}