@burdenoff/website-sdk 2026.517.4 → 2026.517.6

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.
@@ -76,8 +76,11 @@ declare function useContentPages(options?: {
76
76
  refetch: () => void;
77
77
  };
78
78
  /**
79
- * Simple content renderer component.
80
- * Renders markdown content as HTML (assumes pre-rendered HTML or plain text).
79
+ * Content renderer component.
80
+ *
81
+ * - `markdown` (default): converts to HTML via {@link renderMarkdown}
82
+ * - `html`: rendered as-is (assumed already sanitized upstream)
83
+ * - anything else: plain-text fallback
81
84
  *
82
85
  * @example
83
86
  * ```tsx
@@ -76,8 +76,11 @@ declare function useContentPages(options?: {
76
76
  refetch: () => void;
77
77
  };
78
78
  /**
79
- * Simple content renderer component.
80
- * Renders markdown content as HTML (assumes pre-rendered HTML or plain text).
79
+ * Content renderer component.
80
+ *
81
+ * - `markdown` (default): converts to HTML via {@link renderMarkdown}
82
+ * - `html`: rendered as-is (assumed already sanitized upstream)
83
+ * - anything else: plain-text fallback
81
84
  *
82
85
  * @example
83
86
  * ```tsx
@@ -311,12 +311,59 @@ function useContentPages(options) {
311
311
  }, [fetchPages]);
312
312
  return { data, loading, error, refetch: fetchPages };
313
313
  }
314
+ function renderMarkdown(md) {
315
+ let html = md;
316
+ html = html.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
317
+ html = html.replace(/```(\w*)\n([\s\S]*?)```/g, (_m, _lang, code) => {
318
+ return `<pre><code>${code.trim()}</code></pre>`;
319
+ });
320
+ html = html.replace(/`([^`]+)`/g, "<code>$1</code>");
321
+ html = html.replace(/^###### (.+)$/gm, "<h6>$1</h6>");
322
+ html = html.replace(/^##### (.+)$/gm, "<h5>$1</h5>");
323
+ html = html.replace(/^#### (.+)$/gm, "<h4>$1</h4>");
324
+ html = html.replace(/^### (.+)$/gm, "<h3>$1</h3>");
325
+ html = html.replace(/^## (.+)$/gm, "<h2>$1</h2>");
326
+ html = html.replace(/^# (.+)$/gm, "<h1>$1</h1>");
327
+ html = html.replace(/\*\*\*(.+?)\*\*\*/g, "<strong><em>$1</em></strong>");
328
+ html = html.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
329
+ html = html.replace(/\*(.+?)\*/g, "<em>$1</em>");
330
+ html = html.replace(
331
+ /\[([^\]]+)\]\(([^)]+)\)/g,
332
+ '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'
333
+ );
334
+ html = html.replace(/^&gt; (.+)$/gm, "<blockquote>$1</blockquote>");
335
+ html = html.replace(/(^- .+$(\n^- .+$)*)/gm, (block) => {
336
+ const items = block.split("\n").map((line) => `<li>${line.replace(/^- /, "")}</li>`).join("");
337
+ return `<ul>${items}</ul>`;
338
+ });
339
+ html = html.replace(/(^\d+\. .+$(\n^\d+\. .+$)*)/gm, (block) => {
340
+ const items = block.split("\n").map((line) => `<li>${line.replace(/^\d+\. /, "")}</li>`).join("");
341
+ return `<ol>${items}</ol>`;
342
+ });
343
+ html = html.replace(/^---$/gm, "<hr />");
344
+ html = html.split(/\n\n+/).map((block) => {
345
+ const trimmed = block.trim();
346
+ if (!trimmed) return "";
347
+ if (/^<(h[1-6]|ul|ol|pre|blockquote|hr)/.test(trimmed)) return trimmed;
348
+ return `<p>${trimmed.replace(/\n/g, "<br />")}</p>`;
349
+ }).join("\n");
350
+ return html;
351
+ }
314
352
  function ContentRenderer({
315
353
  content,
316
354
  format = "markdown",
317
355
  className
318
356
  }) {
319
- if (format === "html" || format === "markdown") {
357
+ if (format === "markdown") {
358
+ return /* @__PURE__ */ jsxRuntime.jsx(
359
+ "div",
360
+ {
361
+ className,
362
+ dangerouslySetInnerHTML: { __html: renderMarkdown(content) }
363
+ }
364
+ );
365
+ }
366
+ if (format === "html") {
320
367
  return /* @__PURE__ */ jsxRuntime.jsx(
321
368
  "div",
322
369
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/security.ts","../../src/client/graphql-client.ts","../../src/client/provider.tsx","../../src/product/product-provider.tsx","../../src/content/content-provider.tsx","../../src/seo/page-head.tsx","../../src/content/blog.tsx"],"names":["createContext","useContext","useState","useCallback","useEffect","jsx","Helmet","jsxs","Fragment"],"mappings":"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","file":"index.js","sourcesContent":["const LOOPBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"::1\"]);\nconst RESERVED_HEADERS = new Set([\n \"authorization\",\n \"cookie\",\n \"x-actor-id\",\n \"x-actor-type\",\n \"x-consent-granted-by\",\n \"x-consent-scope\",\n \"x-consent-granted-at\",\n \"x-consent-expires-at\",\n]);\n\nexport function validateGatewayUrl(value: string): string {\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n throw new Error(\"Gateway URL must be a valid absolute URL\");\n }\n\n if (url.protocol === \"https:\") {\n return url.toString();\n }\n\n if (\n url.protocol === \"http:\" &&\n LOOPBACK_HOSTS.has(url.hostname.toLowerCase())\n ) {\n return url.toString();\n }\n\n throw new Error(\"Gateway URL must use HTTPS unless it targets localhost\");\n}\n\nexport function sanitizeHeaders(\n headers?: Record<string, string>,\n): Record<string, string> | undefined {\n if (!headers) return undefined;\n\n return Object.fromEntries(\n Object.entries(headers).filter(\n ([key]) => !RESERVED_HEADERS.has(key.toLowerCase()),\n ),\n );\n}\n","/**\n * Lightweight GraphQL client for the Burdenoff Web SDK.\n * Uses native fetch - zero external dependencies.\n * All calls are unauthenticated (public gateway operations only).\n */\n\nimport { sanitizeHeaders, validateGatewayUrl } from \"./security\";\n\nexport interface WebSDKClientConfig {\n /** Global public gateway URL (e.g. \"https://api.burdenoff.com/global/graphql\") */\n gatewayUrl: string;\n /** Platform product ID for scoping operations */\n productId: string;\n /** Product slug (used for public product queries) */\n productSlug?: string;\n /** reCAPTCHA site key (if not fetched from product config) */\n recaptchaSiteKey?: string;\n /** Rybbit analytics site ID */\n rybbitSiteId?: string;\n /** Custom headers to include in all requests */\n headers?: Record<string, string>;\n}\n\nexport interface GraphQLResponse<T = Record<string, unknown>> {\n data?: T;\n errors?: Array<{\n message: string;\n locations?: Array<{ line: number; column: number }>;\n path?: Array<string | number>;\n extensions?: Record<string, unknown>;\n }>;\n}\n\nexport class WebSDKClient {\n private config: WebSDKClientConfig;\n private queryCache = new Map<\n string,\n { data: GraphQLResponse<unknown>; ts: number }\n >();\n private inflight = new Map<string, Promise<GraphQLResponse<unknown>>>();\n private static CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n private static MAX_CACHE_ENTRIES = 100;\n\n constructor(config: WebSDKClientConfig) {\n this.config = {\n ...config,\n gatewayUrl: validateGatewayUrl(config.gatewayUrl),\n headers: sanitizeHeaders(config.headers),\n };\n }\n\n getConfig(): WebSDKClientConfig {\n return this.config;\n }\n\n /**\n * Execute a GraphQL query (with deduplication and caching)\n */\n async query<T = Record<string, unknown>>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<GraphQLResponse<T>> {\n const cacheKey = JSON.stringify({ query, variables });\n\n // Return cached response if still valid, evict if expired\n const cached = this.queryCache.get(cacheKey);\n if (cached) {\n if (Date.now() - cached.ts < WebSDKClient.CACHE_TTL) {\n return cached.data as GraphQLResponse<T>;\n }\n this.queryCache.delete(cacheKey);\n }\n\n // Deduplicate: if same query is already in-flight, reuse the promise\n const existing = this.inflight.get(cacheKey);\n if (existing) {\n return existing as Promise<GraphQLResponse<T>>;\n }\n\n const promise = this.request<T>(query, variables)\n .then((result) => {\n // Cache successful responses only\n if (result.data && !result.errors?.length) {\n if (this.queryCache.size >= WebSDKClient.MAX_CACHE_ENTRIES) {\n const oldestEntry = this.queryCache.keys().next().value;\n if (oldestEntry) {\n this.queryCache.delete(oldestEntry);\n }\n }\n this.queryCache.set(cacheKey, { data: result, ts: Date.now() });\n }\n return result;\n })\n .finally(() => {\n this.inflight.delete(cacheKey);\n });\n\n this.inflight.set(cacheKey, promise);\n return promise;\n }\n\n /**\n * Execute a GraphQL mutation\n */\n async mutate<T = Record<string, unknown>>(\n mutation: string,\n variables?: Record<string, unknown>,\n ): Promise<GraphQLResponse<T>> {\n return this.request<T>(mutation, variables);\n }\n\n private async request<T>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<GraphQLResponse<T>> {\n try {\n const response = await fetch(this.config.gatewayUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-product-id\": this.config.productId,\n ...this.config.headers,\n },\n body: JSON.stringify({ query, variables }),\n });\n\n if (!response.ok) {\n console.error(\n \"[WebSDK] GraphQL request failed\",\n response.status,\n response.statusText,\n );\n return {\n errors: [\n {\n message: `HTTP ${response.status}: ${response.statusText}`,\n extensions: { code: `HTTP_${response.status}` },\n },\n ],\n };\n }\n\n return (await response.json()) as GraphQLResponse<T>;\n } catch (error) {\n console.error(\n \"[WebSDK] Network error\",\n error instanceof Error ? error.message : \"unknown error\",\n );\n return {\n errors: [\n {\n message: error instanceof Error ? error.message : \"Network error\",\n extensions: { code: \"NETWORK_ERROR\" },\n },\n ],\n };\n }\n }\n}\n","/**\n * React context provider for the Web SDK client.\n * Wraps the application with WebSDKClient configuration.\n */\n\"use client\";\n\nimport { createContext, useContext, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\n\nimport { resolveGatewayUrl } from \"./gateway-resolver\";\nimport { WebSDKClient, type WebSDKClientConfig } from \"./graphql-client\";\n\nconst WebSDKContext = createContext<WebSDKClient | null>(null);\n\nexport interface WebSDKProviderProps {\n /** SDK client configuration */\n config: WebSDKClientConfig;\n /** Child components */\n children: ReactNode;\n}\n\n/**\n * Provider component that makes the WebSDKClient available to all child components.\n *\n * @example\n * ```tsx\n * <WebSDKProvider config={{\n * gatewayUrl: \"https://api.burdenoff.com/global/graphql\",\n * productId: \"your-product-id\",\n * productSlug: \"your-product\",\n * }}>\n * <App />\n * </WebSDKProvider>\n * ```\n */\nexport function WebSDKProvider({ config, children }: WebSDKProviderProps) {\n const client = useMemo(() => {\n const resolvedConfig = {\n ...config,\n gatewayUrl: resolveGatewayUrl(config.gatewayUrl),\n };\n return new WebSDKClient(resolvedConfig);\n }, [config]);\n\n return (\n <WebSDKContext.Provider value={client}>{children}</WebSDKContext.Provider>\n );\n}\n\n/**\n * Hook to access the WebSDKClient instance.\n * Must be used within a WebSDKProvider.\n */\nexport function useWebSDK(): WebSDKClient {\n const client = useContext(WebSDKContext);\n if (!client) {\n throw new Error(\"useWebSDK must be used within a WebSDKProvider\");\n }\n return client;\n}\n\n/**\n * Hook to access the SDK configuration.\n * Useful for components that need config values like productId, recaptchaSiteKey, etc.\n */\nexport function useWebSDKConfig(): WebSDKClientConfig {\n const client = useWebSDK();\n return client.getConfig();\n}\n","/**\n * Product Provider\n *\n * React context and hooks for accessing product metadata from the global tenant service.\n * Fetches product info (name, logo, SEO, contact, reCAPTCHA key, etc.) on mount\n * and provides it to all child components.\n */\n\"use client\";\n\nimport {\n createContext,\n useContext,\n useState,\n useLayoutEffect,\n useCallback,\n useMemo,\n} from \"react\";\nimport type { ReactNode } from \"react\";\n\nimport { useWebSDK, useWebSDKConfig } from \"../client/provider\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ProductData {\n id: string;\n name: string;\n slug: string;\n description?: string | null;\n status: string;\n logoUrl?: string | null;\n website?: string | null;\n appUrl?: string | null;\n docsUrl?: string | null;\n contactEmail?: string | null;\n supportEmail?: string | null;\n contactPhone?: string | null;\n contactAddress?: string | null;\n seoTitle?: string | null;\n seoDescription?: string | null;\n seoKeywords?: string | null;\n ogImage?: string | null;\n socialLinks?: Record<string, string> | null;\n recaptchaSiteKey?: string | null;\n rybbitSiteId?: string | null;\n notificationEmail?: string | null;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface ProductProviderProps {\n /** Child components */\n children: ReactNode;\n /** Optional fallback product data (used while loading) */\n fallback?: Partial<ProductData>;\n}\n\n// ============================================================================\n// GraphQL Query\n// ============================================================================\n\nconst PUBLIC_PRODUCT_QUERY = `\n query PublicProduct($slug: String!) {\n publicProduct(slug: $slug) {\n id\n name\n slug\n description\n status\n logoUrl\n website\n appUrl\n docsUrl\n contactEmail\n supportEmail\n contactPhone\n contactAddress\n seoTitle\n seoDescription\n seoKeywords\n ogImage\n socialLinks\n recaptchaSiteKey\n rybbitSiteId\n notificationEmail\n metadata\n }\n }\n`;\n\n// ============================================================================\n// Context\n// ============================================================================\n\ninterface ProductContextValue {\n product: ProductData | null;\n loading: boolean;\n error: string | null;\n hasProvider: boolean;\n refetch: () => void;\n}\n\nconst ProductContext = createContext<ProductContextValue>({\n product: null,\n loading: true,\n error: null,\n hasProvider: false,\n refetch: () => {},\n});\n\n// ============================================================================\n// Provider\n// ============================================================================\n\n/**\n * ProductProvider fetches and provides product metadata to child components.\n * Uses the product slug from WebSDKConfig to fetch from the public API.\n *\n * @example\n * ```tsx\n * <WebSDKProvider config={sdkConfig}>\n * <ProductProvider>\n * <App />\n * </ProductProvider>\n * </WebSDKProvider>\n * ```\n */\nexport function ProductProvider({ children, fallback }: ProductProviderProps) {\n const client = useWebSDK();\n const config = useWebSDKConfig();\n const [product, setProduct] = useState<ProductData | null>(\n fallback ? (fallback as ProductData) : null,\n );\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const fetchProduct = useCallback(async () => {\n if (!config.productSlug) {\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await client.query<{ publicProduct: ProductData | null }>(\n PUBLIC_PRODUCT_QUERY,\n { slug: config.productSlug },\n );\n\n if (result.errors?.length) {\n setError(result.errors[0]?.message ?? \"Unknown error\");\n } else if (result.data?.publicProduct) {\n setProduct(result.data.publicProduct);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to fetch product\");\n } finally {\n setLoading(false);\n }\n }, [client, config.productSlug]);\n\n // Use useLayoutEffect for earliest possible fetch after commit (before paint).\n // Runs when slug or client changes. Client-side query cache + dedup ensures\n // no duplicate network requests even if React StrictMode fires this twice.\n useLayoutEffect(() => {\n fetchProduct();\n }, [fetchProduct]);\n\n const value = useMemo(\n () => ({\n product,\n loading,\n error,\n hasProvider: true,\n refetch: fetchProduct,\n }),\n [product, loading, error, fetchProduct],\n );\n\n return (\n <ProductContext.Provider value={value}>{children}</ProductContext.Provider>\n );\n}\n\n// ============================================================================\n// Hooks\n// ============================================================================\n\n/**\n * Hook to access the current product data.\n *\n * @example\n * ```tsx\n * const { product, loading } = useProduct();\n * if (loading) return <Loading />;\n * return <h1>{product?.name}</h1>;\n * ```\n */\nexport function useProduct(): ProductContextValue {\n return useContext(ProductContext);\n}\n\n/**\n * Hook to get product-specific configuration.\n * Merges SDK config with fetched product data.\n * Useful for getting reCAPTCHA keys, analytics IDs, etc.\n *\n * @example\n * ```tsx\n * const { recaptchaSiteKey, rybbitSiteId } = useProductConfig();\n * ```\n */\nexport function useProductConfig(): {\n recaptchaSiteKey: string | null;\n rybbitSiteId: string | null;\n contactEmail: string | null;\n supportEmail: string | null;\n notificationEmail: string | null;\n productName: string | null;\n logoUrl: string | null;\n} {\n const { product } = useProduct();\n const config = useWebSDKConfig();\n\n return {\n // Prefer product-level config, fall back to SDK config\n recaptchaSiteKey:\n product?.recaptchaSiteKey ?? config.recaptchaSiteKey ?? null,\n rybbitSiteId: product?.rybbitSiteId ?? config.rybbitSiteId ?? null,\n contactEmail: product?.contactEmail ?? null,\n supportEmail: product?.supportEmail ?? null,\n notificationEmail:\n product?.notificationEmail ?? product?.contactEmail ?? null,\n productName: product?.name ?? null,\n logoUrl: product?.logoUrl ?? null,\n };\n}\n","/**\n * Content Provider\n *\n * React hooks for fetching public content pages from the global tenant service.\n * Content pages include privacy policies, terms of service, blogs, changelogs, etc.\n */\n\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\n\nimport { useWebSDK, useWebSDKConfig } from \"../client/provider\";\nimport { useProduct } from \"../product/product-provider\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ContentPageData {\n id: string;\n title: string;\n slug: string;\n content: string;\n contentFormat: string;\n seoTitle?: string | null;\n seoDescription?: string | null;\n seoKeywords?: string | null;\n lastUpdated: string;\n version: number;\n tags?: string[];\n excerpt?: string | null;\n thumbnail?: string | null;\n author?: string | null;\n publishedAt?: string | null;\n readingTime?: number | null;\n featured?: boolean;\n category?: string | null;\n}\n\nexport interface ContentPageListItem {\n id: string;\n title: string;\n slug: string;\n lastUpdated: string;\n tags?: string[];\n excerpt?: string | null;\n thumbnail?: string | null;\n author?: string | null;\n publishedAt?: string | null;\n readingTime?: number | null;\n featured?: boolean;\n category?: string | null;\n seoDescription?: string | null;\n}\n\nexport interface ContentPageListData {\n items: ContentPageListItem[];\n total: number;\n hasMore: boolean;\n}\n\nexport interface UseContentPageOptions {\n /** Whether to use template variable replacement (resolvedContentPage) */\n resolve?: boolean;\n}\n\n// ============================================================================\n// GraphQL Queries\n// ============================================================================\n\nconst PUBLIC_CONTENT_PAGE_QUERY = `\n query PublicContentPage($slug: String!, $productId: String) {\n publicContentPage(slug: $slug, productId: $productId) {\n id\n title\n slug\n content\n contentFormat\n seoTitle\n seoDescription\n seoKeywords\n lastUpdated\n version\n excerpt\n thumbnail\n author\n publishedAt\n readingTime\n featured\n category\n }\n }\n`;\n\nconst PUBLIC_RESOLVED_CONTENT_PAGE_QUERY = `\n query PublicResolvedContentPage($slug: String!, $productId: String!) {\n publicResolvedContentPage(slug: $slug, productId: $productId) {\n id\n title\n slug\n content\n contentFormat\n seoTitle\n seoDescription\n seoKeywords\n lastUpdated\n version\n excerpt\n thumbnail\n author\n publishedAt\n readingTime\n featured\n category\n }\n }\n`;\n\nconst PUBLIC_CONTENT_PAGES_QUERY = `\n query PublicContentPages($productId: String!, $limit: Int, $offset: Int) {\n publicContentPages(productId: $productId, limit: $limit, offset: $offset) {\n items {\n id\n title\n slug\n lastUpdated\n tags\n seoDescription\n excerpt\n thumbnail\n author\n publishedAt\n readingTime\n featured\n category\n }\n total\n hasMore\n }\n }\n`;\n\n// ============================================================================\n// Hooks\n// ============================================================================\n\n/**\n * Hook to fetch a single public content page by slug.\n *\n * @example\n * ```tsx\n * const { data, loading, error } = useContentPage('privacy-policy');\n * ```\n */\nexport function useContentPage(\n slug: string,\n options?: UseContentPageOptions,\n): {\n data: ContentPageData | null;\n loading: boolean;\n error: string | null;\n refetch: () => void;\n} {\n const client = useWebSDK();\n const config = useWebSDKConfig();\n const { product } = useProduct();\n const resolvedProductId = product?.id || config.productId;\n const [data, setData] = useState<ContentPageData | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n const fetchPage = useCallback(async () => {\n if (!resolvedProductId) return; // Wait for product to load\n\n setLoading(true);\n setError(null);\n\n try {\n const query = options?.resolve\n ? PUBLIC_RESOLVED_CONTENT_PAGE_QUERY\n : PUBLIC_CONTENT_PAGE_QUERY;\n\n const queryName = options?.resolve\n ? \"publicResolvedContentPage\"\n : \"publicContentPage\";\n\n const result = await client.query<Record<string, ContentPageData | null>>(\n query,\n {\n slug,\n productId: resolvedProductId,\n },\n );\n\n if (result.errors?.length) {\n setError(result.errors[0]?.message ?? \"Unknown error\");\n setData(null);\n } else {\n setData(result.data?.[queryName] ?? null);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to fetch content\");\n setData(null);\n } finally {\n setLoading(false);\n }\n }, [client, resolvedProductId, slug, options?.resolve]);\n\n useEffect(() => {\n fetchPage();\n }, [fetchPage]);\n\n return { data, loading, error, refetch: fetchPage };\n}\n\n/**\n * Hook to fetch a list of public content pages for the current product.\n *\n * @example\n * ```tsx\n * const { data, loading } = useContentPages({ limit: 10 });\n * ```\n */\nexport function useContentPages(options?: {\n limit?: number;\n offset?: number;\n}): {\n data: ContentPageListData | null;\n loading: boolean;\n error: string | null;\n refetch: () => void;\n} {\n const client = useWebSDK();\n const config = useWebSDKConfig();\n const [data, setData] = useState<ContentPageListData | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n const fetchPages = useCallback(async () => {\n setLoading(true);\n setError(null);\n\n try {\n const result = await client.query<{\n publicContentPages: ContentPageListData;\n }>(PUBLIC_CONTENT_PAGES_QUERY, {\n productId: config.productId,\n limit: options?.limit ?? 50,\n offset: options?.offset ?? 0,\n });\n\n if (result.errors?.length) {\n setError(result.errors[0]?.message ?? \"Unknown error\");\n setData(null);\n } else {\n setData(result.data?.publicContentPages ?? null);\n }\n } catch (err) {\n setError(\n err instanceof Error ? err.message : \"Failed to fetch content pages\",\n );\n setData(null);\n } finally {\n setLoading(false);\n }\n }, [client, config.productId, options?.limit, options?.offset]);\n\n useEffect(() => {\n fetchPages();\n }, [fetchPages]);\n\n return { data, loading, error, refetch: fetchPages };\n}\n\n/**\n * Simple content renderer component.\n * Renders markdown content as HTML (assumes pre-rendered HTML or plain text).\n *\n * @example\n * ```tsx\n * <ContentRenderer content={page.content} format={page.contentFormat} />\n * ```\n */\nexport function ContentRenderer({\n content,\n format = \"markdown\",\n className,\n}: {\n content: string;\n format?: string;\n className?: string;\n}) {\n if (format === \"html\" || format === \"markdown\") {\n // For both HTML and markdown, we render as HTML\n // (markdown should be pre-rendered to HTML by the backend)\n return (\n <div\n className={className}\n dangerouslySetInnerHTML={{ __html: content }}\n />\n );\n }\n\n // Plain text fallback\n return (\n <div className={className}>\n <pre style={{ whiteSpace: \"pre-wrap\", fontFamily: \"inherit\" }}>\n {content}\n </pre>\n </div>\n );\n}\n","/**\n * PageHead Component\n * Manages dynamic meta tags for SEO optimization on a per-page basis.\n * Supports JSON-LD structured data, Open Graph, Twitter Cards, favicons, and robots directives.\n *\n * @example\n * ```tsx\n * <PageHead\n * title=\"About Us\"\n * description=\"Learn about our mission and team\"\n * productName=\"MyProduct\"\n * keywords=\"about, team, mission\"\n * image=\"/og-about.png\"\n * url=\"https://myproduct.com/about\"\n * favicon=\"/favicon.ico\"\n * robots=\"index, follow\"\n * locale=\"en_US\"\n * twitterHandle=\"@burdenoff\"\n * themeColor=\"#1a1a2e\"\n * structuredData={{\n * \"@context\": \"https://schema.org\",\n * \"@type\": \"WebPage\",\n * name: \"About Us\",\n * }}\n * />\n * ```\n */\n\nimport { Helmet } from \"react-helmet-async\";\n\nexport interface PageHeadProps {\n /** Page title */\n title: string;\n /** Meta description */\n description?: string;\n /** Product/brand name (appended to title) */\n productName?: string;\n /** SEO keywords */\n keywords?: string;\n /** Open Graph image URL */\n image?: string;\n /** Page canonical URL */\n url?: string;\n /** Favicon URL */\n favicon?: string;\n /** Robots directive (e.g., \"index, follow\") */\n robots?: string;\n /** Open Graph locale (e.g., \"en_US\") */\n locale?: string;\n /** Twitter handle (e.g., \"@burdenoff\") */\n twitterHandle?: string;\n /** Theme color for mobile browsers */\n themeColor?: string;\n /** JSON-LD structured data objects */\n structuredData?: Record<string, unknown> | Array<Record<string, unknown>>;\n /** Additional meta tags */\n additionalMeta?: Array<{\n name?: string;\n property?: string;\n content: string;\n }>;\n}\n\nexport function PageHead({\n title,\n description,\n productName,\n keywords,\n image,\n url,\n favicon,\n robots = \"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\",\n locale = \"en_US\",\n twitterHandle,\n themeColor = \"#000000\",\n structuredData,\n additionalMeta,\n}: PageHeadProps) {\n const fullTitle =\n productName && !title.includes(productName)\n ? `${title} | ${productName}`\n : title;\n\n const fullImageUrl =\n image && !image.startsWith(\"http\") && url\n ? `${url.replace(/\\/$/, \"\")}${image}`\n : image;\n\n // Normalize structured data to an array\n const structuredDataItems: Array<Record<string, unknown>> = structuredData\n ? Array.isArray(structuredData)\n ? structuredData\n : [structuredData]\n : [];\n\n return (\n <Helmet>\n {/* Primary Meta Tags */}\n <title>{fullTitle}</title>\n <meta name=\"title\" content={fullTitle} />\n {description && <meta name=\"description\" content={description} />}\n {keywords && <meta name=\"keywords\" content={keywords} />}\n <meta name=\"author\" content=\"Burdenoff Consultancy Services Pvt Ltd\" />\n <meta name=\"robots\" content={robots} />\n\n {/* Canonical URL */}\n {url && <link rel=\"canonical\" href={url} />}\n\n {/* Favicon */}\n {favicon && <link rel=\"icon\" href={favicon} />}\n\n {/* Open Graph / Facebook */}\n <meta property=\"og:type\" content=\"website\" />\n <meta property=\"og:title\" content={fullTitle} />\n {description && <meta property=\"og:description\" content={description} />}\n {fullImageUrl && <meta property=\"og:image\" content={fullImageUrl} />}\n {url && <meta property=\"og:url\" content={url} />}\n <meta property=\"og:locale\" content={locale} />\n {productName && <meta property=\"og:site_name\" content={productName} />}\n\n {/* Twitter Card */}\n <meta name=\"twitter:card\" content=\"summary_large_image\" />\n <meta name=\"twitter:title\" content={fullTitle} />\n {description && <meta name=\"twitter:description\" content={description} />}\n {fullImageUrl && <meta name=\"twitter:image\" content={fullImageUrl} />}\n {twitterHandle && <meta name=\"twitter:creator\" content={twitterHandle} />}\n\n {/* Theme Color */}\n <meta name=\"theme-color\" content={themeColor} />\n\n {/* JSON-LD Structured Data */}\n {structuredDataItems.map((data, index) => (\n <script key={`ld-${index}`} type=\"application/ld+json\">\n {JSON.stringify(data)}\n </script>\n ))}\n\n {/* Additional Meta Tags */}\n {additionalMeta?.map((meta, index) => (\n <meta\n key={`meta-${index}`}\n {...(meta.name ? { name: meta.name } : {})}\n {...(meta.property ? { property: meta.property } : {})}\n content={meta.content}\n />\n ))}\n </Helmet>\n );\n}\n","\"use client\";\n\n/**\n * Blog Components\n *\n * CMS-powered blog list and post pages. Uses ProductContentPage with tag \"blog\".\n *\n * @example\n * ```tsx\n * import { BlogListPage, BlogPostPage } from '@burdenoff/website-sdk'\n *\n * <Route path=\"/blog\" element={<BlogListPage productName=\"VibeControls\" />} />\n * <Route path=\"/blog/:slug\" element={<BlogPostPage productName=\"VibeControls\" />} />\n * ```\n */\n\nimport { useState, useEffect, useCallback } from \"react\";\n\nimport { useWebSDK, useWebSDKConfig } from \"../client/provider\";\nimport { useProduct } from \"../product/product-provider\";\nimport { ContentRenderer } from \"./content-provider\";\nimport { PageHead } from \"../seo/page-head\";\nimport type { ContentPageData, ContentPageListItem } from \"./content-provider\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BlogListPageProps {\n productName?: string;\n heroTitle?: string;\n heroSubtitle?: string;\n blogPostBasePath?: string;\n className?: string;\n seoTitle?: string;\n seoDescription?: string;\n seoKeywords?: string;\n}\n\nexport interface BlogPostPageProps {\n productName?: string;\n blogListUrl?: string;\n className?: string;\n}\n\n// ============================================================================\n// Query\n// ============================================================================\n\nconst BLOG_POSTS_QUERY = `\n query BlogPosts($productId: String!, $limit: Int, $offset: Int) {\n publicContentPages(productId: $productId, limit: $limit, offset: $offset) {\n items {\n id\n title\n slug\n lastUpdated\n tags\n seoDescription\n excerpt\n thumbnail\n author\n publishedAt\n readingTime\n featured\n category\n }\n total\n hasMore\n }\n }\n`;\n\nconst BLOG_POST_QUERY = `\n query BlogPost($slug: String!, $productId: String) {\n publicContentPage(slug: $slug, productId: $productId) {\n id\n title\n slug\n content\n contentFormat\n seoTitle\n seoDescription\n seoKeywords\n lastUpdated\n tags\n excerpt\n thumbnail\n author\n publishedAt\n readingTime\n featured\n category\n }\n }\n`;\n\n// ============================================================================\n// BlogCard\n// ============================================================================\n\nfunction BlogCard({\n post,\n basePath = \"/blog\",\n}: {\n post: ContentPageListItem;\n basePath?: string;\n}) {\n const date = post.publishedAt || post.lastUpdated;\n const formattedDate = date\n ? new Date(date).toLocaleDateString(\"en-US\", {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n })\n : \"\";\n\n return (\n <a\n href={`${basePath}/${post.slug}`}\n className=\"group block bg-card border rounded-xl overflow-hidden hover:shadow-lg hover:border-primary/30 transition-all duration-300\"\n >\n {/* Thumbnail */}\n {post.thumbnail ? (\n <div className=\"aspect-[16/9] overflow-hidden\">\n <img\n src={post.thumbnail}\n alt={post.title}\n className=\"w-full h-full object-cover group-hover:scale-105 transition-transform duration-300\"\n loading=\"lazy\"\n />\n </div>\n ) : (\n <div className=\"aspect-[16/9] bg-muted/50 flex items-center justify-center\">\n <svg\n className=\"w-12 h-12 text-muted-foreground/20\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253\"\n />\n </svg>\n </div>\n )}\n\n <div className=\"p-5 sm:p-6\">\n {/* Category badge */}\n {post.category && (\n <span className=\"inline-block text-xs font-medium text-primary bg-primary/10 px-2.5 py-1 rounded-full mb-3\">\n {post.category}\n </span>\n )}\n\n {/* Title */}\n <h3 className=\"text-lg font-semibold text-foreground mb-2 group-hover:text-primary transition-colors line-clamp-2\">\n {post.title}\n </h3>\n\n {/* Excerpt */}\n <p className=\"text-sm text-muted-foreground mb-4 line-clamp-3\">\n {post.excerpt || post.seoDescription || \"\"}\n </p>\n\n {/* Footer: author + date + reading time */}\n <div className=\"flex items-center gap-3 text-xs text-muted-foreground\">\n {post.author && <span className=\"font-medium\">{post.author}</span>}\n {post.author && formattedDate && (\n <span className=\"text-muted-foreground/40\">·</span>\n )}\n {formattedDate && <span>{formattedDate}</span>}\n {post.readingTime && (\n <>\n <span className=\"text-muted-foreground/40\">·</span>\n <span>{post.readingTime} min read</span>\n </>\n )}\n </div>\n </div>\n </a>\n );\n}\n\n// ============================================================================\n// BlogListPage\n// ============================================================================\n\nexport function BlogListPage(props: BlogListPageProps) {\n const {\n productName,\n heroTitle = \"Blog\",\n heroSubtitle = \"Latest updates, engineering insights, and product news\",\n blogPostBasePath = \"/blog\",\n className = \"\",\n seoTitle,\n seoDescription,\n seoKeywords,\n } = props;\n\n const client = useWebSDK();\n const config = useWebSDKConfig();\n const { product } = useProduct();\n const resolvedProductId = product?.id || config.productId;\n\n const [posts, setPosts] = useState<ContentPageListItem[]>([]);\n const [total, setTotal] = useState(0);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [offset, setOffset] = useState(0);\n const limit = 12;\n\n const fetchPosts = useCallback(async () => {\n if (!resolvedProductId) return;\n\n setLoading(true);\n try {\n const result = await client.query<{\n publicContentPages: {\n items: ContentPageListItem[];\n total: number;\n hasMore: boolean;\n };\n }>(BLOG_POSTS_QUERY, {\n productId: resolvedProductId,\n limit,\n offset,\n });\n\n if (result.errors?.length) {\n setError(result.errors[0]?.message ?? \"Failed to load posts\");\n return;\n }\n\n // Filter to only blog-tagged posts\n const blogPosts = (result.data?.publicContentPages?.items || []).filter(\n (p) => p.tags?.includes(\"blog\"),\n );\n\n setPosts(blogPosts);\n setTotal(result.data?.publicContentPages?.total || 0);\n setError(null);\n } catch {\n setError(\"Failed to load blog posts\");\n } finally {\n setLoading(false);\n }\n }, [client, resolvedProductId, offset]);\n\n useEffect(() => {\n fetchPosts();\n }, [fetchPosts]);\n\n // Separate featured and regular posts\n const featuredPosts = posts.filter((p) => p.featured);\n const regularPosts = posts.filter((p) => !p.featured);\n\n return (\n <div className={`w-full ${className}`}>\n {seoTitle && (\n <PageHead\n title={seoTitle}\n description={seoDescription || heroSubtitle}\n productName={productName}\n keywords={seoKeywords}\n />\n )}\n\n {/* Hero */}\n <section className=\"py-16 sm:py-20 md:py-24\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n <div className=\"text-center max-w-3xl mx-auto\">\n <h1 className=\"text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground\">\n {heroTitle}\n </h1>\n <p className=\"mt-4 text-lg text-muted-foreground\">{heroSubtitle}</p>\n </div>\n </div>\n </section>\n\n {/* Posts */}\n <section className=\"pb-16 sm:pb-20\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n {loading ? (\n <div className=\"grid md:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8\">\n {[1, 2, 3].map((i) => (\n <div\n key={i}\n className=\"bg-card border rounded-xl overflow-hidden animate-pulse\"\n >\n <div className=\"aspect-[16/9] bg-muted\" />\n <div className=\"p-6 space-y-3\">\n <div className=\"h-4 bg-muted rounded w-1/4\" />\n <div className=\"h-6 bg-muted rounded w-3/4\" />\n <div className=\"h-4 bg-muted rounded\" />\n <div className=\"h-4 bg-muted rounded w-1/2\" />\n </div>\n </div>\n ))}\n </div>\n ) : error ? (\n <div className=\"text-center py-12\">\n <p className=\"text-muted-foreground\">{error}</p>\n </div>\n ) : posts.length === 0 ? (\n <div className=\"text-center py-12\">\n <p className=\"text-muted-foreground\">\n No blog posts yet. Check back soon!\n </p>\n </div>\n ) : (\n <>\n {/* Featured posts (full width) */}\n {featuredPosts.length > 0 && (\n <div className=\"grid md:grid-cols-2 gap-6 sm:gap-8 mb-8\">\n {featuredPosts.map((post) => (\n <BlogCard\n key={post.id}\n post={post}\n basePath={blogPostBasePath}\n />\n ))}\n </div>\n )}\n\n {/* Regular posts (3-column grid) */}\n <div className=\"grid md:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8\">\n {regularPosts.map((post) => (\n <BlogCard\n key={post.id}\n post={post}\n basePath={blogPostBasePath}\n />\n ))}\n </div>\n\n {/* Pagination */}\n {total > limit && (\n <div className=\"flex justify-center gap-4 mt-12\">\n <button\n onClick={() => setOffset(Math.max(0, offset - limit))}\n disabled={offset === 0}\n className=\"px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors\"\n >\n Previous\n </button>\n <button\n onClick={() => setOffset(offset + limit)}\n disabled={offset + limit >= total}\n className=\"px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors\"\n >\n Next\n </button>\n </div>\n )}\n </>\n )}\n </div>\n </section>\n </div>\n );\n}\n\n// ============================================================================\n// BlogPostPage\n// ============================================================================\n\nexport function BlogPostPage(props: BlogPostPageProps) {\n const { productName, blogListUrl = \"/blog\", className = \"\" } = props;\n\n const client = useWebSDK();\n const config = useWebSDKConfig();\n const { product } = useProduct();\n const resolvedProductId = product?.id || config.productId;\n\n const [post, setPost] = useState<ContentPageData | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n // Get slug from URL\n const slug =\n typeof window !== \"undefined\"\n ? window.location.pathname.split(\"/\").pop() || \"\"\n : \"\";\n\n useEffect(() => {\n if (!slug || !resolvedProductId) return;\n\n async function fetchPost() {\n setLoading(true);\n try {\n const result = await client.query<{\n publicContentPage: ContentPageData | null;\n }>(BLOG_POST_QUERY, {\n slug,\n productId: resolvedProductId,\n });\n\n if (result.errors?.length) {\n setError(result.errors[0]?.message ?? \"Failed to load post\");\n return;\n }\n\n setPost(result.data?.publicContentPage ?? null);\n setError(null);\n } catch {\n setError(\"Failed to load blog post\");\n } finally {\n setLoading(false);\n }\n }\n\n fetchPost();\n }, [client, slug, resolvedProductId]);\n\n if (loading) {\n return (\n <div className={`w-full ${className}`}>\n <div className=\"max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-24\">\n <div className=\"animate-pulse space-y-6\">\n <div className=\"h-8 bg-muted rounded w-2/3\" />\n <div className=\"h-4 bg-muted rounded w-1/3\" />\n <div className=\"aspect-[16/9] bg-muted rounded-xl\" />\n <div className=\"space-y-3\">\n {[1, 2, 3, 4, 5].map((i) => (\n <div key={i} className=\"h-4 bg-muted rounded\" />\n ))}\n </div>\n </div>\n </div>\n </div>\n );\n }\n\n if (error || !post) {\n return (\n <div className={`w-full ${className}`}>\n <div className=\"max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-24 text-center\">\n <h1 className=\"text-2xl font-bold mb-4\">Post not found</h1>\n <p className=\"text-muted-foreground mb-6\">\n {error || \"This blog post doesn't exist or has been removed.\"}\n </p>\n <a\n href={blogListUrl}\n className=\"text-primary hover:underline font-medium\"\n >\n ← Back to Blog\n </a>\n </div>\n </div>\n );\n }\n\n const date = post.publishedAt || post.lastUpdated;\n const formattedDate = date\n ? new Date(date).toLocaleDateString(\"en-US\", {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n })\n : \"\";\n\n return (\n <div className={`w-full ${className}`}>\n <PageHead\n title={post.seoTitle || post.title}\n description={post.seoDescription || post.excerpt || \"\"}\n productName={productName}\n keywords={post.seoKeywords || \"\"}\n />\n\n <article className=\"max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16 sm:py-20\">\n {/* Back link */}\n <a\n href={blogListUrl}\n className=\"inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground mb-8 transition-colors\"\n >\n ← Back to Blog\n </a>\n\n {/* Category */}\n {post.category && (\n <span className=\"inline-block text-xs font-medium text-primary bg-primary/10 px-2.5 py-1 rounded-full mb-4\">\n {post.category}\n </span>\n )}\n\n {/* Title */}\n <h1 className=\"text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground mb-4\">\n {post.title}\n </h1>\n\n {/* Meta */}\n <div className=\"flex items-center gap-3 text-sm text-muted-foreground mb-8\">\n {post.author && (\n <span className=\"font-medium text-foreground\">{post.author}</span>\n )}\n {post.author && formattedDate && (\n <span className=\"text-muted-foreground/40\">·</span>\n )}\n {formattedDate && <span>{formattedDate}</span>}\n {post.readingTime && (\n <>\n <span className=\"text-muted-foreground/40\">·</span>\n <span>{post.readingTime} min read</span>\n </>\n )}\n </div>\n\n {/* Hero image */}\n {post.thumbnail && (\n <div className=\"aspect-[16/9] rounded-xl overflow-hidden mb-10\">\n <img\n src={post.thumbnail}\n alt={post.title}\n className=\"w-full h-full object-cover\"\n />\n </div>\n )}\n\n {/* Content */}\n <div className=\"prose prose-gray dark:prose-invert max-w-none\">\n <ContentRenderer\n content={post.content}\n format={post.contentFormat}\n />\n </div>\n </article>\n </div>\n );\n}\n"]}
1
+ {"version":3,"sources":["../../src/client/security.ts","../../src/client/graphql-client.ts","../../src/client/provider.tsx","../../src/product/product-provider.tsx","../../src/content/content-provider.tsx","../../src/seo/page-head.tsx","../../src/content/blog.tsx"],"names":["createContext","useContext","useState","useCallback","useEffect","jsx","Helmet","jsxs","Fragment"],"mappings":"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","file":"index.js","sourcesContent":["const LOOPBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"::1\"]);\nconst RESERVED_HEADERS = new Set([\n \"authorization\",\n \"cookie\",\n \"x-actor-id\",\n \"x-actor-type\",\n \"x-consent-granted-by\",\n \"x-consent-scope\",\n \"x-consent-granted-at\",\n \"x-consent-expires-at\",\n]);\n\nexport function validateGatewayUrl(value: string): string {\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n throw new Error(\"Gateway URL must be a valid absolute URL\");\n }\n\n if (url.protocol === \"https:\") {\n return url.toString();\n }\n\n if (\n url.protocol === \"http:\" &&\n LOOPBACK_HOSTS.has(url.hostname.toLowerCase())\n ) {\n return url.toString();\n }\n\n throw new Error(\"Gateway URL must use HTTPS unless it targets localhost\");\n}\n\nexport function sanitizeHeaders(\n headers?: Record<string, string>,\n): Record<string, string> | undefined {\n if (!headers) return undefined;\n\n return Object.fromEntries(\n Object.entries(headers).filter(\n ([key]) => !RESERVED_HEADERS.has(key.toLowerCase()),\n ),\n );\n}\n","/**\n * Lightweight GraphQL client for the Burdenoff Web SDK.\n * Uses native fetch - zero external dependencies.\n * All calls are unauthenticated (public gateway operations only).\n */\n\nimport { sanitizeHeaders, validateGatewayUrl } from \"./security\";\n\nexport interface WebSDKClientConfig {\n /** Global public gateway URL (e.g. \"https://api.burdenoff.com/global/graphql\") */\n gatewayUrl: string;\n /** Platform product ID for scoping operations */\n productId: string;\n /** Product slug (used for public product queries) */\n productSlug?: string;\n /** reCAPTCHA site key (if not fetched from product config) */\n recaptchaSiteKey?: string;\n /** Rybbit analytics site ID */\n rybbitSiteId?: string;\n /** Custom headers to include in all requests */\n headers?: Record<string, string>;\n}\n\nexport interface GraphQLResponse<T = Record<string, unknown>> {\n data?: T;\n errors?: Array<{\n message: string;\n locations?: Array<{ line: number; column: number }>;\n path?: Array<string | number>;\n extensions?: Record<string, unknown>;\n }>;\n}\n\nexport class WebSDKClient {\n private config: WebSDKClientConfig;\n private queryCache = new Map<\n string,\n { data: GraphQLResponse<unknown>; ts: number }\n >();\n private inflight = new Map<string, Promise<GraphQLResponse<unknown>>>();\n private static CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n private static MAX_CACHE_ENTRIES = 100;\n\n constructor(config: WebSDKClientConfig) {\n this.config = {\n ...config,\n gatewayUrl: validateGatewayUrl(config.gatewayUrl),\n headers: sanitizeHeaders(config.headers),\n };\n }\n\n getConfig(): WebSDKClientConfig {\n return this.config;\n }\n\n /**\n * Execute a GraphQL query (with deduplication and caching)\n */\n async query<T = Record<string, unknown>>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<GraphQLResponse<T>> {\n const cacheKey = JSON.stringify({ query, variables });\n\n // Return cached response if still valid, evict if expired\n const cached = this.queryCache.get(cacheKey);\n if (cached) {\n if (Date.now() - cached.ts < WebSDKClient.CACHE_TTL) {\n return cached.data as GraphQLResponse<T>;\n }\n this.queryCache.delete(cacheKey);\n }\n\n // Deduplicate: if same query is already in-flight, reuse the promise\n const existing = this.inflight.get(cacheKey);\n if (existing) {\n return existing as Promise<GraphQLResponse<T>>;\n }\n\n const promise = this.request<T>(query, variables)\n .then((result) => {\n // Cache successful responses only\n if (result.data && !result.errors?.length) {\n if (this.queryCache.size >= WebSDKClient.MAX_CACHE_ENTRIES) {\n const oldestEntry = this.queryCache.keys().next().value;\n if (oldestEntry) {\n this.queryCache.delete(oldestEntry);\n }\n }\n this.queryCache.set(cacheKey, { data: result, ts: Date.now() });\n }\n return result;\n })\n .finally(() => {\n this.inflight.delete(cacheKey);\n });\n\n this.inflight.set(cacheKey, promise);\n return promise;\n }\n\n /**\n * Execute a GraphQL mutation\n */\n async mutate<T = Record<string, unknown>>(\n mutation: string,\n variables?: Record<string, unknown>,\n ): Promise<GraphQLResponse<T>> {\n return this.request<T>(mutation, variables);\n }\n\n private async request<T>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<GraphQLResponse<T>> {\n try {\n const response = await fetch(this.config.gatewayUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-product-id\": this.config.productId,\n ...this.config.headers,\n },\n body: JSON.stringify({ query, variables }),\n });\n\n if (!response.ok) {\n console.error(\n \"[WebSDK] GraphQL request failed\",\n response.status,\n response.statusText,\n );\n return {\n errors: [\n {\n message: `HTTP ${response.status}: ${response.statusText}`,\n extensions: { code: `HTTP_${response.status}` },\n },\n ],\n };\n }\n\n return (await response.json()) as GraphQLResponse<T>;\n } catch (error) {\n console.error(\n \"[WebSDK] Network error\",\n error instanceof Error ? error.message : \"unknown error\",\n );\n return {\n errors: [\n {\n message: error instanceof Error ? error.message : \"Network error\",\n extensions: { code: \"NETWORK_ERROR\" },\n },\n ],\n };\n }\n }\n}\n","/**\n * React context provider for the Web SDK client.\n * Wraps the application with WebSDKClient configuration.\n */\n\"use client\";\n\nimport { createContext, useContext, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\n\nimport { resolveGatewayUrl } from \"./gateway-resolver\";\nimport { WebSDKClient, type WebSDKClientConfig } from \"./graphql-client\";\n\nconst WebSDKContext = createContext<WebSDKClient | null>(null);\n\nexport interface WebSDKProviderProps {\n /** SDK client configuration */\n config: WebSDKClientConfig;\n /** Child components */\n children: ReactNode;\n}\n\n/**\n * Provider component that makes the WebSDKClient available to all child components.\n *\n * @example\n * ```tsx\n * <WebSDKProvider config={{\n * gatewayUrl: \"https://api.burdenoff.com/global/graphql\",\n * productId: \"your-product-id\",\n * productSlug: \"your-product\",\n * }}>\n * <App />\n * </WebSDKProvider>\n * ```\n */\nexport function WebSDKProvider({ config, children }: WebSDKProviderProps) {\n const client = useMemo(() => {\n const resolvedConfig = {\n ...config,\n gatewayUrl: resolveGatewayUrl(config.gatewayUrl),\n };\n return new WebSDKClient(resolvedConfig);\n }, [config]);\n\n return (\n <WebSDKContext.Provider value={client}>{children}</WebSDKContext.Provider>\n );\n}\n\n/**\n * Hook to access the WebSDKClient instance.\n * Must be used within a WebSDKProvider.\n */\nexport function useWebSDK(): WebSDKClient {\n const client = useContext(WebSDKContext);\n if (!client) {\n throw new Error(\"useWebSDK must be used within a WebSDKProvider\");\n }\n return client;\n}\n\n/**\n * Hook to access the SDK configuration.\n * Useful for components that need config values like productId, recaptchaSiteKey, etc.\n */\nexport function useWebSDKConfig(): WebSDKClientConfig {\n const client = useWebSDK();\n return client.getConfig();\n}\n","/**\n * Product Provider\n *\n * React context and hooks for accessing product metadata from the global tenant service.\n * Fetches product info (name, logo, SEO, contact, reCAPTCHA key, etc.) on mount\n * and provides it to all child components.\n */\n\"use client\";\n\nimport {\n createContext,\n useContext,\n useState,\n useLayoutEffect,\n useCallback,\n useMemo,\n} from \"react\";\nimport type { ReactNode } from \"react\";\n\nimport { useWebSDK, useWebSDKConfig } from \"../client/provider\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ProductData {\n id: string;\n name: string;\n slug: string;\n description?: string | null;\n status: string;\n logoUrl?: string | null;\n website?: string | null;\n appUrl?: string | null;\n docsUrl?: string | null;\n contactEmail?: string | null;\n supportEmail?: string | null;\n contactPhone?: string | null;\n contactAddress?: string | null;\n seoTitle?: string | null;\n seoDescription?: string | null;\n seoKeywords?: string | null;\n ogImage?: string | null;\n socialLinks?: Record<string, string> | null;\n recaptchaSiteKey?: string | null;\n rybbitSiteId?: string | null;\n notificationEmail?: string | null;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface ProductProviderProps {\n /** Child components */\n children: ReactNode;\n /** Optional fallback product data (used while loading) */\n fallback?: Partial<ProductData>;\n}\n\n// ============================================================================\n// GraphQL Query\n// ============================================================================\n\nconst PUBLIC_PRODUCT_QUERY = `\n query PublicProduct($slug: String!) {\n publicProduct(slug: $slug) {\n id\n name\n slug\n description\n status\n logoUrl\n website\n appUrl\n docsUrl\n contactEmail\n supportEmail\n contactPhone\n contactAddress\n seoTitle\n seoDescription\n seoKeywords\n ogImage\n socialLinks\n recaptchaSiteKey\n rybbitSiteId\n notificationEmail\n metadata\n }\n }\n`;\n\n// ============================================================================\n// Context\n// ============================================================================\n\ninterface ProductContextValue {\n product: ProductData | null;\n loading: boolean;\n error: string | null;\n hasProvider: boolean;\n refetch: () => void;\n}\n\nconst ProductContext = createContext<ProductContextValue>({\n product: null,\n loading: true,\n error: null,\n hasProvider: false,\n refetch: () => {},\n});\n\n// ============================================================================\n// Provider\n// ============================================================================\n\n/**\n * ProductProvider fetches and provides product metadata to child components.\n * Uses the product slug from WebSDKConfig to fetch from the public API.\n *\n * @example\n * ```tsx\n * <WebSDKProvider config={sdkConfig}>\n * <ProductProvider>\n * <App />\n * </ProductProvider>\n * </WebSDKProvider>\n * ```\n */\nexport function ProductProvider({ children, fallback }: ProductProviderProps) {\n const client = useWebSDK();\n const config = useWebSDKConfig();\n const [product, setProduct] = useState<ProductData | null>(\n fallback ? (fallback as ProductData) : null,\n );\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const fetchProduct = useCallback(async () => {\n if (!config.productSlug) {\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await client.query<{ publicProduct: ProductData | null }>(\n PUBLIC_PRODUCT_QUERY,\n { slug: config.productSlug },\n );\n\n if (result.errors?.length) {\n setError(result.errors[0]?.message ?? \"Unknown error\");\n } else if (result.data?.publicProduct) {\n setProduct(result.data.publicProduct);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to fetch product\");\n } finally {\n setLoading(false);\n }\n }, [client, config.productSlug]);\n\n // Use useLayoutEffect for earliest possible fetch after commit (before paint).\n // Runs when slug or client changes. Client-side query cache + dedup ensures\n // no duplicate network requests even if React StrictMode fires this twice.\n useLayoutEffect(() => {\n fetchProduct();\n }, [fetchProduct]);\n\n const value = useMemo(\n () => ({\n product,\n loading,\n error,\n hasProvider: true,\n refetch: fetchProduct,\n }),\n [product, loading, error, fetchProduct],\n );\n\n return (\n <ProductContext.Provider value={value}>{children}</ProductContext.Provider>\n );\n}\n\n// ============================================================================\n// Hooks\n// ============================================================================\n\n/**\n * Hook to access the current product data.\n *\n * @example\n * ```tsx\n * const { product, loading } = useProduct();\n * if (loading) return <Loading />;\n * return <h1>{product?.name}</h1>;\n * ```\n */\nexport function useProduct(): ProductContextValue {\n return useContext(ProductContext);\n}\n\n/**\n * Hook to get product-specific configuration.\n * Merges SDK config with fetched product data.\n * Useful for getting reCAPTCHA keys, analytics IDs, etc.\n *\n * @example\n * ```tsx\n * const { recaptchaSiteKey, rybbitSiteId } = useProductConfig();\n * ```\n */\nexport function useProductConfig(): {\n recaptchaSiteKey: string | null;\n rybbitSiteId: string | null;\n contactEmail: string | null;\n supportEmail: string | null;\n notificationEmail: string | null;\n productName: string | null;\n logoUrl: string | null;\n} {\n const { product } = useProduct();\n const config = useWebSDKConfig();\n\n return {\n // Prefer product-level config, fall back to SDK config\n recaptchaSiteKey:\n product?.recaptchaSiteKey ?? config.recaptchaSiteKey ?? null,\n rybbitSiteId: product?.rybbitSiteId ?? config.rybbitSiteId ?? null,\n contactEmail: product?.contactEmail ?? null,\n supportEmail: product?.supportEmail ?? null,\n notificationEmail:\n product?.notificationEmail ?? product?.contactEmail ?? null,\n productName: product?.name ?? null,\n logoUrl: product?.logoUrl ?? null,\n };\n}\n","/**\n * Content Provider\n *\n * React hooks for fetching public content pages from the global tenant service.\n * Content pages include privacy policies, terms of service, blogs, changelogs, etc.\n */\n\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\n\nimport { useWebSDK, useWebSDKConfig } from \"../client/provider\";\nimport { useProduct } from \"../product/product-provider\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ContentPageData {\n id: string;\n title: string;\n slug: string;\n content: string;\n contentFormat: string;\n seoTitle?: string | null;\n seoDescription?: string | null;\n seoKeywords?: string | null;\n lastUpdated: string;\n version: number;\n tags?: string[];\n excerpt?: string | null;\n thumbnail?: string | null;\n author?: string | null;\n publishedAt?: string | null;\n readingTime?: number | null;\n featured?: boolean;\n category?: string | null;\n}\n\nexport interface ContentPageListItem {\n id: string;\n title: string;\n slug: string;\n lastUpdated: string;\n tags?: string[];\n excerpt?: string | null;\n thumbnail?: string | null;\n author?: string | null;\n publishedAt?: string | null;\n readingTime?: number | null;\n featured?: boolean;\n category?: string | null;\n seoDescription?: string | null;\n}\n\nexport interface ContentPageListData {\n items: ContentPageListItem[];\n total: number;\n hasMore: boolean;\n}\n\nexport interface UseContentPageOptions {\n /** Whether to use template variable replacement (resolvedContentPage) */\n resolve?: boolean;\n}\n\n// ============================================================================\n// GraphQL Queries\n// ============================================================================\n\nconst PUBLIC_CONTENT_PAGE_QUERY = `\n query PublicContentPage($slug: String!, $productId: String) {\n publicContentPage(slug: $slug, productId: $productId) {\n id\n title\n slug\n content\n contentFormat\n seoTitle\n seoDescription\n seoKeywords\n lastUpdated\n version\n excerpt\n thumbnail\n author\n publishedAt\n readingTime\n featured\n category\n }\n }\n`;\n\nconst PUBLIC_RESOLVED_CONTENT_PAGE_QUERY = `\n query PublicResolvedContentPage($slug: String!, $productId: String!) {\n publicResolvedContentPage(slug: $slug, productId: $productId) {\n id\n title\n slug\n content\n contentFormat\n seoTitle\n seoDescription\n seoKeywords\n lastUpdated\n version\n excerpt\n thumbnail\n author\n publishedAt\n readingTime\n featured\n category\n }\n }\n`;\n\nconst PUBLIC_CONTENT_PAGES_QUERY = `\n query PublicContentPages($productId: String!, $limit: Int, $offset: Int) {\n publicContentPages(productId: $productId, limit: $limit, offset: $offset) {\n items {\n id\n title\n slug\n lastUpdated\n tags\n seoDescription\n excerpt\n thumbnail\n author\n publishedAt\n readingTime\n featured\n category\n }\n total\n hasMore\n }\n }\n`;\n\n// ============================================================================\n// Hooks\n// ============================================================================\n\n/**\n * Hook to fetch a single public content page by slug.\n *\n * @example\n * ```tsx\n * const { data, loading, error } = useContentPage('privacy-policy');\n * ```\n */\nexport function useContentPage(\n slug: string,\n options?: UseContentPageOptions,\n): {\n data: ContentPageData | null;\n loading: boolean;\n error: string | null;\n refetch: () => void;\n} {\n const client = useWebSDK();\n const config = useWebSDKConfig();\n const { product } = useProduct();\n const resolvedProductId = product?.id || config.productId;\n const [data, setData] = useState<ContentPageData | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n const fetchPage = useCallback(async () => {\n if (!resolvedProductId) return; // Wait for product to load\n\n setLoading(true);\n setError(null);\n\n try {\n const query = options?.resolve\n ? PUBLIC_RESOLVED_CONTENT_PAGE_QUERY\n : PUBLIC_CONTENT_PAGE_QUERY;\n\n const queryName = options?.resolve\n ? \"publicResolvedContentPage\"\n : \"publicContentPage\";\n\n const result = await client.query<Record<string, ContentPageData | null>>(\n query,\n {\n slug,\n productId: resolvedProductId,\n },\n );\n\n if (result.errors?.length) {\n setError(result.errors[0]?.message ?? \"Unknown error\");\n setData(null);\n } else {\n setData(result.data?.[queryName] ?? null);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to fetch content\");\n setData(null);\n } finally {\n setLoading(false);\n }\n }, [client, resolvedProductId, slug, options?.resolve]);\n\n useEffect(() => {\n fetchPage();\n }, [fetchPage]);\n\n return { data, loading, error, refetch: fetchPage };\n}\n\n/**\n * Hook to fetch a list of public content pages for the current product.\n *\n * @example\n * ```tsx\n * const { data, loading } = useContentPages({ limit: 10 });\n * ```\n */\nexport function useContentPages(options?: {\n limit?: number;\n offset?: number;\n}): {\n data: ContentPageListData | null;\n loading: boolean;\n error: string | null;\n refetch: () => void;\n} {\n const client = useWebSDK();\n const config = useWebSDKConfig();\n const [data, setData] = useState<ContentPageListData | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n const fetchPages = useCallback(async () => {\n setLoading(true);\n setError(null);\n\n try {\n const result = await client.query<{\n publicContentPages: ContentPageListData;\n }>(PUBLIC_CONTENT_PAGES_QUERY, {\n productId: config.productId,\n limit: options?.limit ?? 50,\n offset: options?.offset ?? 0,\n });\n\n if (result.errors?.length) {\n setError(result.errors[0]?.message ?? \"Unknown error\");\n setData(null);\n } else {\n setData(result.data?.publicContentPages ?? null);\n }\n } catch (err) {\n setError(\n err instanceof Error ? err.message : \"Failed to fetch content pages\",\n );\n setData(null);\n } finally {\n setLoading(false);\n }\n }, [client, config.productId, options?.limit, options?.offset]);\n\n useEffect(() => {\n fetchPages();\n }, [fetchPages]);\n\n return { data, loading, error, refetch: fetchPages };\n}\n\n/**\n * Minimal, dependency-free Markdown → HTML renderer.\n *\n * HTML entities are escaped first so raw user content cannot inject markup\n * (the output is fed to dangerouslySetInnerHTML). Mirrors the admin content\n * editor's preview renderer so authored content looks the same everywhere.\n */\nfunction renderMarkdown(md: string): string {\n let html = md;\n\n html = html\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\");\n\n // Fenced code blocks\n html = html.replace(/```(\\w*)\\n([\\s\\S]*?)```/g, (_m, _lang, code) => {\n return `<pre><code>${code.trim()}</code></pre>`;\n });\n // Inline code\n html = html.replace(/`([^`]+)`/g, \"<code>$1</code>\");\n\n // Headings\n html = html.replace(/^###### (.+)$/gm, \"<h6>$1</h6>\");\n html = html.replace(/^##### (.+)$/gm, \"<h5>$1</h5>\");\n html = html.replace(/^#### (.+)$/gm, \"<h4>$1</h4>\");\n html = html.replace(/^### (.+)$/gm, \"<h3>$1</h3>\");\n html = html.replace(/^## (.+)$/gm, \"<h2>$1</h2>\");\n html = html.replace(/^# (.+)$/gm, \"<h1>$1</h1>\");\n\n // Bold / italic\n html = html.replace(/\\*\\*\\*(.+?)\\*\\*\\*/g, \"<strong><em>$1</em></strong>\");\n html = html.replace(/\\*\\*(.+?)\\*\\*/g, \"<strong>$1</strong>\");\n html = html.replace(/\\*(.+?)\\*/g, \"<em>$1</em>\");\n\n // Links\n html = html.replace(\n /\\[([^\\]]+)\\]\\(([^)]+)\\)/g,\n '<a href=\"$2\" target=\"_blank\" rel=\"noopener noreferrer\">$1</a>',\n );\n\n // Blockquotes (entities already escaped → match &gt;)\n html = html.replace(/^&gt; (.+)$/gm, \"<blockquote>$1</blockquote>\");\n\n // Unordered lists\n html = html.replace(/(^- .+$(\\n^- .+$)*)/gm, (block) => {\n const items = block\n .split(\"\\n\")\n .map((line) => `<li>${line.replace(/^- /, \"\")}</li>`)\n .join(\"\");\n return `<ul>${items}</ul>`;\n });\n // Ordered lists\n html = html.replace(/(^\\d+\\. .+$(\\n^\\d+\\. .+$)*)/gm, (block) => {\n const items = block\n .split(\"\\n\")\n .map((line) => `<li>${line.replace(/^\\d+\\. /, \"\")}</li>`)\n .join(\"\");\n return `<ol>${items}</ol>`;\n });\n\n // Horizontal rule\n html = html.replace(/^---$/gm, \"<hr />\");\n\n // Paragraphs\n html = html\n .split(/\\n\\n+/)\n .map((block) => {\n const trimmed = block.trim();\n if (!trimmed) return \"\";\n if (/^<(h[1-6]|ul|ol|pre|blockquote|hr)/.test(trimmed)) return trimmed;\n return `<p>${trimmed.replace(/\\n/g, \"<br />\")}</p>`;\n })\n .join(\"\\n\");\n\n return html;\n}\n\n/**\n * Content renderer component.\n *\n * - `markdown` (default): converts to HTML via {@link renderMarkdown}\n * - `html`: rendered as-is (assumed already sanitized upstream)\n * - anything else: plain-text fallback\n *\n * @example\n * ```tsx\n * <ContentRenderer content={page.content} format={page.contentFormat} />\n * ```\n */\nexport function ContentRenderer({\n content,\n format = \"markdown\",\n className,\n}: {\n content: string;\n format?: string;\n className?: string;\n}) {\n if (format === \"markdown\") {\n return (\n <div\n className={className}\n dangerouslySetInnerHTML={{ __html: renderMarkdown(content) }}\n />\n );\n }\n\n if (format === \"html\") {\n return (\n <div\n className={className}\n dangerouslySetInnerHTML={{ __html: content }}\n />\n );\n }\n\n // Plain text fallback\n return (\n <div className={className}>\n <pre style={{ whiteSpace: \"pre-wrap\", fontFamily: \"inherit\" }}>\n {content}\n </pre>\n </div>\n );\n}\n","/**\n * PageHead Component\n * Manages dynamic meta tags for SEO optimization on a per-page basis.\n * Supports JSON-LD structured data, Open Graph, Twitter Cards, favicons, and robots directives.\n *\n * @example\n * ```tsx\n * <PageHead\n * title=\"About Us\"\n * description=\"Learn about our mission and team\"\n * productName=\"MyProduct\"\n * keywords=\"about, team, mission\"\n * image=\"/og-about.png\"\n * url=\"https://myproduct.com/about\"\n * favicon=\"/favicon.ico\"\n * robots=\"index, follow\"\n * locale=\"en_US\"\n * twitterHandle=\"@burdenoff\"\n * themeColor=\"#1a1a2e\"\n * structuredData={{\n * \"@context\": \"https://schema.org\",\n * \"@type\": \"WebPage\",\n * name: \"About Us\",\n * }}\n * />\n * ```\n */\n\nimport { Helmet } from \"react-helmet-async\";\n\nexport interface PageHeadProps {\n /** Page title */\n title: string;\n /** Meta description */\n description?: string;\n /** Product/brand name (appended to title) */\n productName?: string;\n /** SEO keywords */\n keywords?: string;\n /** Open Graph image URL */\n image?: string;\n /** Page canonical URL */\n url?: string;\n /** Favicon URL */\n favicon?: string;\n /** Robots directive (e.g., \"index, follow\") */\n robots?: string;\n /** Open Graph locale (e.g., \"en_US\") */\n locale?: string;\n /** Twitter handle (e.g., \"@burdenoff\") */\n twitterHandle?: string;\n /** Theme color for mobile browsers */\n themeColor?: string;\n /** JSON-LD structured data objects */\n structuredData?: Record<string, unknown> | Array<Record<string, unknown>>;\n /** Additional meta tags */\n additionalMeta?: Array<{\n name?: string;\n property?: string;\n content: string;\n }>;\n}\n\nexport function PageHead({\n title,\n description,\n productName,\n keywords,\n image,\n url,\n favicon,\n robots = \"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\",\n locale = \"en_US\",\n twitterHandle,\n themeColor = \"#000000\",\n structuredData,\n additionalMeta,\n}: PageHeadProps) {\n const fullTitle =\n productName && !title.includes(productName)\n ? `${title} | ${productName}`\n : title;\n\n const fullImageUrl =\n image && !image.startsWith(\"http\") && url\n ? `${url.replace(/\\/$/, \"\")}${image}`\n : image;\n\n // Normalize structured data to an array\n const structuredDataItems: Array<Record<string, unknown>> = structuredData\n ? Array.isArray(structuredData)\n ? structuredData\n : [structuredData]\n : [];\n\n return (\n <Helmet>\n {/* Primary Meta Tags */}\n <title>{fullTitle}</title>\n <meta name=\"title\" content={fullTitle} />\n {description && <meta name=\"description\" content={description} />}\n {keywords && <meta name=\"keywords\" content={keywords} />}\n <meta name=\"author\" content=\"Burdenoff Consultancy Services Pvt Ltd\" />\n <meta name=\"robots\" content={robots} />\n\n {/* Canonical URL */}\n {url && <link rel=\"canonical\" href={url} />}\n\n {/* Favicon */}\n {favicon && <link rel=\"icon\" href={favicon} />}\n\n {/* Open Graph / Facebook */}\n <meta property=\"og:type\" content=\"website\" />\n <meta property=\"og:title\" content={fullTitle} />\n {description && <meta property=\"og:description\" content={description} />}\n {fullImageUrl && <meta property=\"og:image\" content={fullImageUrl} />}\n {url && <meta property=\"og:url\" content={url} />}\n <meta property=\"og:locale\" content={locale} />\n {productName && <meta property=\"og:site_name\" content={productName} />}\n\n {/* Twitter Card */}\n <meta name=\"twitter:card\" content=\"summary_large_image\" />\n <meta name=\"twitter:title\" content={fullTitle} />\n {description && <meta name=\"twitter:description\" content={description} />}\n {fullImageUrl && <meta name=\"twitter:image\" content={fullImageUrl} />}\n {twitterHandle && <meta name=\"twitter:creator\" content={twitterHandle} />}\n\n {/* Theme Color */}\n <meta name=\"theme-color\" content={themeColor} />\n\n {/* JSON-LD Structured Data */}\n {structuredDataItems.map((data, index) => (\n <script key={`ld-${index}`} type=\"application/ld+json\">\n {JSON.stringify(data)}\n </script>\n ))}\n\n {/* Additional Meta Tags */}\n {additionalMeta?.map((meta, index) => (\n <meta\n key={`meta-${index}`}\n {...(meta.name ? { name: meta.name } : {})}\n {...(meta.property ? { property: meta.property } : {})}\n content={meta.content}\n />\n ))}\n </Helmet>\n );\n}\n","\"use client\";\n\n/**\n * Blog Components\n *\n * CMS-powered blog list and post pages. Uses ProductContentPage with tag \"blog\".\n *\n * @example\n * ```tsx\n * import { BlogListPage, BlogPostPage } from '@burdenoff/website-sdk'\n *\n * <Route path=\"/blog\" element={<BlogListPage productName=\"VibeControls\" />} />\n * <Route path=\"/blog/:slug\" element={<BlogPostPage productName=\"VibeControls\" />} />\n * ```\n */\n\nimport { useState, useEffect, useCallback } from \"react\";\n\nimport { useWebSDK, useWebSDKConfig } from \"../client/provider\";\nimport { useProduct } from \"../product/product-provider\";\nimport { ContentRenderer } from \"./content-provider\";\nimport { PageHead } from \"../seo/page-head\";\nimport type { ContentPageData, ContentPageListItem } from \"./content-provider\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BlogListPageProps {\n productName?: string;\n heroTitle?: string;\n heroSubtitle?: string;\n blogPostBasePath?: string;\n className?: string;\n seoTitle?: string;\n seoDescription?: string;\n seoKeywords?: string;\n}\n\nexport interface BlogPostPageProps {\n productName?: string;\n blogListUrl?: string;\n className?: string;\n}\n\n// ============================================================================\n// Query\n// ============================================================================\n\nconst BLOG_POSTS_QUERY = `\n query BlogPosts($productId: String!, $limit: Int, $offset: Int) {\n publicContentPages(productId: $productId, limit: $limit, offset: $offset) {\n items {\n id\n title\n slug\n lastUpdated\n tags\n seoDescription\n excerpt\n thumbnail\n author\n publishedAt\n readingTime\n featured\n category\n }\n total\n hasMore\n }\n }\n`;\n\nconst BLOG_POST_QUERY = `\n query BlogPost($slug: String!, $productId: String) {\n publicContentPage(slug: $slug, productId: $productId) {\n id\n title\n slug\n content\n contentFormat\n seoTitle\n seoDescription\n seoKeywords\n lastUpdated\n tags\n excerpt\n thumbnail\n author\n publishedAt\n readingTime\n featured\n category\n }\n }\n`;\n\n// ============================================================================\n// BlogCard\n// ============================================================================\n\nfunction BlogCard({\n post,\n basePath = \"/blog\",\n}: {\n post: ContentPageListItem;\n basePath?: string;\n}) {\n const date = post.publishedAt || post.lastUpdated;\n const formattedDate = date\n ? new Date(date).toLocaleDateString(\"en-US\", {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n })\n : \"\";\n\n return (\n <a\n href={`${basePath}/${post.slug}`}\n className=\"group block bg-card border rounded-xl overflow-hidden hover:shadow-lg hover:border-primary/30 transition-all duration-300\"\n >\n {/* Thumbnail */}\n {post.thumbnail ? (\n <div className=\"aspect-[16/9] overflow-hidden\">\n <img\n src={post.thumbnail}\n alt={post.title}\n className=\"w-full h-full object-cover group-hover:scale-105 transition-transform duration-300\"\n loading=\"lazy\"\n />\n </div>\n ) : (\n <div className=\"aspect-[16/9] bg-muted/50 flex items-center justify-center\">\n <svg\n className=\"w-12 h-12 text-muted-foreground/20\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253\"\n />\n </svg>\n </div>\n )}\n\n <div className=\"p-5 sm:p-6\">\n {/* Category badge */}\n {post.category && (\n <span className=\"inline-block text-xs font-medium text-primary bg-primary/10 px-2.5 py-1 rounded-full mb-3\">\n {post.category}\n </span>\n )}\n\n {/* Title */}\n <h3 className=\"text-lg font-semibold text-foreground mb-2 group-hover:text-primary transition-colors line-clamp-2\">\n {post.title}\n </h3>\n\n {/* Excerpt */}\n <p className=\"text-sm text-muted-foreground mb-4 line-clamp-3\">\n {post.excerpt || post.seoDescription || \"\"}\n </p>\n\n {/* Footer: author + date + reading time */}\n <div className=\"flex items-center gap-3 text-xs text-muted-foreground\">\n {post.author && <span className=\"font-medium\">{post.author}</span>}\n {post.author && formattedDate && (\n <span className=\"text-muted-foreground/40\">·</span>\n )}\n {formattedDate && <span>{formattedDate}</span>}\n {post.readingTime && (\n <>\n <span className=\"text-muted-foreground/40\">·</span>\n <span>{post.readingTime} min read</span>\n </>\n )}\n </div>\n </div>\n </a>\n );\n}\n\n// ============================================================================\n// BlogListPage\n// ============================================================================\n\nexport function BlogListPage(props: BlogListPageProps) {\n const {\n productName,\n heroTitle = \"Blog\",\n heroSubtitle = \"Latest updates, engineering insights, and product news\",\n blogPostBasePath = \"/blog\",\n className = \"\",\n seoTitle,\n seoDescription,\n seoKeywords,\n } = props;\n\n const client = useWebSDK();\n const config = useWebSDKConfig();\n const { product } = useProduct();\n const resolvedProductId = product?.id || config.productId;\n\n const [posts, setPosts] = useState<ContentPageListItem[]>([]);\n const [total, setTotal] = useState(0);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [offset, setOffset] = useState(0);\n const limit = 12;\n\n const fetchPosts = useCallback(async () => {\n if (!resolvedProductId) return;\n\n setLoading(true);\n try {\n const result = await client.query<{\n publicContentPages: {\n items: ContentPageListItem[];\n total: number;\n hasMore: boolean;\n };\n }>(BLOG_POSTS_QUERY, {\n productId: resolvedProductId,\n limit,\n offset,\n });\n\n if (result.errors?.length) {\n setError(result.errors[0]?.message ?? \"Failed to load posts\");\n return;\n }\n\n // Filter to only blog-tagged posts\n const blogPosts = (result.data?.publicContentPages?.items || []).filter(\n (p) => p.tags?.includes(\"blog\"),\n );\n\n setPosts(blogPosts);\n setTotal(result.data?.publicContentPages?.total || 0);\n setError(null);\n } catch {\n setError(\"Failed to load blog posts\");\n } finally {\n setLoading(false);\n }\n }, [client, resolvedProductId, offset]);\n\n useEffect(() => {\n fetchPosts();\n }, [fetchPosts]);\n\n // Separate featured and regular posts\n const featuredPosts = posts.filter((p) => p.featured);\n const regularPosts = posts.filter((p) => !p.featured);\n\n return (\n <div className={`w-full ${className}`}>\n {seoTitle && (\n <PageHead\n title={seoTitle}\n description={seoDescription || heroSubtitle}\n productName={productName}\n keywords={seoKeywords}\n />\n )}\n\n {/* Hero */}\n <section className=\"py-16 sm:py-20 md:py-24\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n <div className=\"text-center max-w-3xl mx-auto\">\n <h1 className=\"text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground\">\n {heroTitle}\n </h1>\n <p className=\"mt-4 text-lg text-muted-foreground\">{heroSubtitle}</p>\n </div>\n </div>\n </section>\n\n {/* Posts */}\n <section className=\"pb-16 sm:pb-20\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n {loading ? (\n <div className=\"grid md:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8\">\n {[1, 2, 3].map((i) => (\n <div\n key={i}\n className=\"bg-card border rounded-xl overflow-hidden animate-pulse\"\n >\n <div className=\"aspect-[16/9] bg-muted\" />\n <div className=\"p-6 space-y-3\">\n <div className=\"h-4 bg-muted rounded w-1/4\" />\n <div className=\"h-6 bg-muted rounded w-3/4\" />\n <div className=\"h-4 bg-muted rounded\" />\n <div className=\"h-4 bg-muted rounded w-1/2\" />\n </div>\n </div>\n ))}\n </div>\n ) : error ? (\n <div className=\"text-center py-12\">\n <p className=\"text-muted-foreground\">{error}</p>\n </div>\n ) : posts.length === 0 ? (\n <div className=\"text-center py-12\">\n <p className=\"text-muted-foreground\">\n No blog posts yet. Check back soon!\n </p>\n </div>\n ) : (\n <>\n {/* Featured posts (full width) */}\n {featuredPosts.length > 0 && (\n <div className=\"grid md:grid-cols-2 gap-6 sm:gap-8 mb-8\">\n {featuredPosts.map((post) => (\n <BlogCard\n key={post.id}\n post={post}\n basePath={blogPostBasePath}\n />\n ))}\n </div>\n )}\n\n {/* Regular posts (3-column grid) */}\n <div className=\"grid md:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8\">\n {regularPosts.map((post) => (\n <BlogCard\n key={post.id}\n post={post}\n basePath={blogPostBasePath}\n />\n ))}\n </div>\n\n {/* Pagination */}\n {total > limit && (\n <div className=\"flex justify-center gap-4 mt-12\">\n <button\n onClick={() => setOffset(Math.max(0, offset - limit))}\n disabled={offset === 0}\n className=\"px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors\"\n >\n Previous\n </button>\n <button\n onClick={() => setOffset(offset + limit)}\n disabled={offset + limit >= total}\n className=\"px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors\"\n >\n Next\n </button>\n </div>\n )}\n </>\n )}\n </div>\n </section>\n </div>\n );\n}\n\n// ============================================================================\n// BlogPostPage\n// ============================================================================\n\nexport function BlogPostPage(props: BlogPostPageProps) {\n const { productName, blogListUrl = \"/blog\", className = \"\" } = props;\n\n const client = useWebSDK();\n const config = useWebSDKConfig();\n const { product } = useProduct();\n const resolvedProductId = product?.id || config.productId;\n\n const [post, setPost] = useState<ContentPageData | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n // Get slug from URL\n const slug =\n typeof window !== \"undefined\"\n ? window.location.pathname.split(\"/\").pop() || \"\"\n : \"\";\n\n useEffect(() => {\n if (!slug || !resolvedProductId) return;\n\n async function fetchPost() {\n setLoading(true);\n try {\n const result = await client.query<{\n publicContentPage: ContentPageData | null;\n }>(BLOG_POST_QUERY, {\n slug,\n productId: resolvedProductId,\n });\n\n if (result.errors?.length) {\n setError(result.errors[0]?.message ?? \"Failed to load post\");\n return;\n }\n\n setPost(result.data?.publicContentPage ?? null);\n setError(null);\n } catch {\n setError(\"Failed to load blog post\");\n } finally {\n setLoading(false);\n }\n }\n\n fetchPost();\n }, [client, slug, resolvedProductId]);\n\n if (loading) {\n return (\n <div className={`w-full ${className}`}>\n <div className=\"max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-24\">\n <div className=\"animate-pulse space-y-6\">\n <div className=\"h-8 bg-muted rounded w-2/3\" />\n <div className=\"h-4 bg-muted rounded w-1/3\" />\n <div className=\"aspect-[16/9] bg-muted rounded-xl\" />\n <div className=\"space-y-3\">\n {[1, 2, 3, 4, 5].map((i) => (\n <div key={i} className=\"h-4 bg-muted rounded\" />\n ))}\n </div>\n </div>\n </div>\n </div>\n );\n }\n\n if (error || !post) {\n return (\n <div className={`w-full ${className}`}>\n <div className=\"max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-24 text-center\">\n <h1 className=\"text-2xl font-bold mb-4\">Post not found</h1>\n <p className=\"text-muted-foreground mb-6\">\n {error || \"This blog post doesn't exist or has been removed.\"}\n </p>\n <a\n href={blogListUrl}\n className=\"text-primary hover:underline font-medium\"\n >\n ← Back to Blog\n </a>\n </div>\n </div>\n );\n }\n\n const date = post.publishedAt || post.lastUpdated;\n const formattedDate = date\n ? new Date(date).toLocaleDateString(\"en-US\", {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n })\n : \"\";\n\n return (\n <div className={`w-full ${className}`}>\n <PageHead\n title={post.seoTitle || post.title}\n description={post.seoDescription || post.excerpt || \"\"}\n productName={productName}\n keywords={post.seoKeywords || \"\"}\n />\n\n <article className=\"max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16 sm:py-20\">\n {/* Back link */}\n <a\n href={blogListUrl}\n className=\"inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground mb-8 transition-colors\"\n >\n ← Back to Blog\n </a>\n\n {/* Category */}\n {post.category && (\n <span className=\"inline-block text-xs font-medium text-primary bg-primary/10 px-2.5 py-1 rounded-full mb-4\">\n {post.category}\n </span>\n )}\n\n {/* Title */}\n <h1 className=\"text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground mb-4\">\n {post.title}\n </h1>\n\n {/* Meta */}\n <div className=\"flex items-center gap-3 text-sm text-muted-foreground mb-8\">\n {post.author && (\n <span className=\"font-medium text-foreground\">{post.author}</span>\n )}\n {post.author && formattedDate && (\n <span className=\"text-muted-foreground/40\">·</span>\n )}\n {formattedDate && <span>{formattedDate}</span>}\n {post.readingTime && (\n <>\n <span className=\"text-muted-foreground/40\">·</span>\n <span>{post.readingTime} min read</span>\n </>\n )}\n </div>\n\n {/* Hero image */}\n {post.thumbnail && (\n <div className=\"aspect-[16/9] rounded-xl overflow-hidden mb-10\">\n <img\n src={post.thumbnail}\n alt={post.title}\n className=\"w-full h-full object-cover\"\n />\n </div>\n )}\n\n {/* Content */}\n <div className=\"prose prose-gray dark:prose-invert max-w-none\">\n <ContentRenderer\n content={post.content}\n format={post.contentFormat}\n />\n </div>\n </article>\n </div>\n );\n}\n"]}
@@ -309,12 +309,59 @@ function useContentPages(options) {
309
309
  }, [fetchPages]);
310
310
  return { data, loading, error, refetch: fetchPages };
311
311
  }
312
+ function renderMarkdown(md) {
313
+ let html = md;
314
+ html = html.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
315
+ html = html.replace(/```(\w*)\n([\s\S]*?)```/g, (_m, _lang, code) => {
316
+ return `<pre><code>${code.trim()}</code></pre>`;
317
+ });
318
+ html = html.replace(/`([^`]+)`/g, "<code>$1</code>");
319
+ html = html.replace(/^###### (.+)$/gm, "<h6>$1</h6>");
320
+ html = html.replace(/^##### (.+)$/gm, "<h5>$1</h5>");
321
+ html = html.replace(/^#### (.+)$/gm, "<h4>$1</h4>");
322
+ html = html.replace(/^### (.+)$/gm, "<h3>$1</h3>");
323
+ html = html.replace(/^## (.+)$/gm, "<h2>$1</h2>");
324
+ html = html.replace(/^# (.+)$/gm, "<h1>$1</h1>");
325
+ html = html.replace(/\*\*\*(.+?)\*\*\*/g, "<strong><em>$1</em></strong>");
326
+ html = html.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
327
+ html = html.replace(/\*(.+?)\*/g, "<em>$1</em>");
328
+ html = html.replace(
329
+ /\[([^\]]+)\]\(([^)]+)\)/g,
330
+ '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'
331
+ );
332
+ html = html.replace(/^&gt; (.+)$/gm, "<blockquote>$1</blockquote>");
333
+ html = html.replace(/(^- .+$(\n^- .+$)*)/gm, (block) => {
334
+ const items = block.split("\n").map((line) => `<li>${line.replace(/^- /, "")}</li>`).join("");
335
+ return `<ul>${items}</ul>`;
336
+ });
337
+ html = html.replace(/(^\d+\. .+$(\n^\d+\. .+$)*)/gm, (block) => {
338
+ const items = block.split("\n").map((line) => `<li>${line.replace(/^\d+\. /, "")}</li>`).join("");
339
+ return `<ol>${items}</ol>`;
340
+ });
341
+ html = html.replace(/^---$/gm, "<hr />");
342
+ html = html.split(/\n\n+/).map((block) => {
343
+ const trimmed = block.trim();
344
+ if (!trimmed) return "";
345
+ if (/^<(h[1-6]|ul|ol|pre|blockquote|hr)/.test(trimmed)) return trimmed;
346
+ return `<p>${trimmed.replace(/\n/g, "<br />")}</p>`;
347
+ }).join("\n");
348
+ return html;
349
+ }
312
350
  function ContentRenderer({
313
351
  content,
314
352
  format = "markdown",
315
353
  className
316
354
  }) {
317
- if (format === "html" || format === "markdown") {
355
+ if (format === "markdown") {
356
+ return /* @__PURE__ */ jsx(
357
+ "div",
358
+ {
359
+ className,
360
+ dangerouslySetInnerHTML: { __html: renderMarkdown(content) }
361
+ }
362
+ );
363
+ }
364
+ if (format === "html") {
318
365
  return /* @__PURE__ */ jsx(
319
366
  "div",
320
367
  {