@burdenoff/website-sdk 2026.829.8 → 2026.921.1

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 (60) hide show
  1. package/README.md +112 -1
  2. package/dist/components/contact.js +4 -1
  3. package/dist/components/contact.js.map +1 -1
  4. package/dist/components/contact.mjs +4 -1
  5. package/dist/components/contact.mjs.map +1 -1
  6. package/dist/components/explore.d.mts +14 -1
  7. package/dist/components/explore.d.ts +14 -1
  8. package/dist/components/explore.js +217 -43
  9. package/dist/components/explore.js.map +1 -1
  10. package/dist/components/explore.mjs +217 -45
  11. package/dist/components/explore.mjs.map +1 -1
  12. package/dist/components/newsletter.js +4 -1
  13. package/dist/components/newsletter.js.map +1 -1
  14. package/dist/components/newsletter.mjs +4 -1
  15. package/dist/components/newsletter.mjs.map +1 -1
  16. package/dist/components/partners.js +23 -13
  17. package/dist/components/partners.js.map +1 -1
  18. package/dist/components/partners.mjs +23 -13
  19. package/dist/components/partners.mjs.map +1 -1
  20. package/dist/components/pricing.js +4 -1
  21. package/dist/components/pricing.js.map +1 -1
  22. package/dist/components/pricing.mjs +4 -1
  23. package/dist/components/pricing.mjs.map +1 -1
  24. package/dist/components/unsubscribe.js +4 -1
  25. package/dist/components/unsubscribe.js.map +1 -1
  26. package/dist/components/unsubscribe.mjs +4 -1
  27. package/dist/components/unsubscribe.mjs.map +1 -1
  28. package/dist/components/waitlist.js +4 -1
  29. package/dist/components/waitlist.js.map +1 -1
  30. package/dist/components/waitlist.mjs +4 -1
  31. package/dist/components/waitlist.mjs.map +1 -1
  32. package/dist/content/index.js +23 -13
  33. package/dist/content/index.js.map +1 -1
  34. package/dist/content/index.mjs +23 -13
  35. package/dist/content/index.mjs.map +1 -1
  36. package/dist/{use-explore-chat-BzXCz2UM.d.mts → explore-types-CLw19R76.d.mts} +1 -131
  37. package/dist/{use-explore-chat-BzXCz2UM.d.ts → explore-types-CLw19R76.d.ts} +1 -131
  38. package/dist/hooks/index.d.mts +2 -1
  39. package/dist/hooks/index.d.ts +2 -1
  40. package/dist/hooks/index.js +23 -7
  41. package/dist/hooks/index.js.map +1 -1
  42. package/dist/hooks/index.mjs +23 -7
  43. package/dist/hooks/index.mjs.map +1 -1
  44. package/dist/index.d.mts +143 -3
  45. package/dist/index.d.ts +143 -3
  46. package/dist/index.js +1552 -207
  47. package/dist/index.js.map +1 -1
  48. package/dist/index.mjs +1548 -210
  49. package/dist/index.mjs.map +1 -1
  50. package/dist/seo/index.js +4 -1
  51. package/dist/seo/index.js.map +1 -1
  52. package/dist/seo/index.mjs +4 -1
  53. package/dist/seo/index.mjs.map +1 -1
  54. package/dist/store/index.js +23 -13
  55. package/dist/store/index.js.map +1 -1
  56. package/dist/store/index.mjs +23 -13
  57. package/dist/store/index.mjs.map +1 -1
  58. package/dist/use-explore-chat-CvW-kyIm.d.mts +145 -0
  59. package/dist/use-explore-chat-DdGBimiz.d.ts +145 -0
  60. package/package.json +1 -1
package/dist/seo/index.js CHANGED
@@ -19,6 +19,9 @@ function resolveImageUrl(image, base) {
19
19
  return image;
20
20
  }
21
21
  }
22
+ function serializeJsonLd(data) {
23
+ return JSON.stringify(data).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
24
+ }
22
25
  function PageHead({
23
26
  title,
24
27
  description,
@@ -60,7 +63,7 @@ function PageHead({
60
63
  fullImageUrl && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:image", content: fullImageUrl }),
61
64
  twitterHandle && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:creator", content: twitterHandle }),
62
65
  /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "theme-color", content: themeColor }),
63
- structuredDataItems.map((data, index) => /* @__PURE__ */ jsxRuntime.jsx("script", { type: "application/ld+json", children: JSON.stringify(data) }, `ld-${index}`)),
66
+ structuredDataItems.map((data, index) => /* @__PURE__ */ jsxRuntime.jsx("script", { type: "application/ld+json", children: serializeJsonLd(data) }, `ld-${index}`)),
64
67
  additionalMeta?.map((meta, index) => /* @__PURE__ */ jsxRuntime.jsx(
65
68
  "meta",
66
69
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/seo/page-head.tsx"],"names":["Helmet","jsx"],"mappings":"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","file":"index.js","sourcesContent":["/**\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\n/**\n * Build a self-referencing canonical URL from the current location.\n *\n * Query strings and hashes are dropped (they never identify a distinct\n * canonical document here) and the trailing slash is normalised away for\n * everything but the site root. Returns `undefined` outside a DOM so the\n * component degrades to its previous behaviour under plain string rendering.\n */\nfunction canonicalFromLocation(): string | undefined {\n if (typeof window === \"undefined\" || !window.location) return undefined;\n const { origin, pathname } = window.location;\n if (!origin || origin === \"null\") return undefined;\n const path = pathname === \"/\" ? \"/\" : pathname.replace(/\\/+$/, \"\");\n return `${origin}${path}`;\n}\n\n/**\n * Absolutise an Open Graph image URL.\n *\n * Already-absolute URLs pass through. A root-relative path resolves against\n * the origin, and any other relative path against the page URL. Without a\n * resolvable base the original value is returned unchanged.\n */\nfunction resolveImageUrl(\n image: string | undefined,\n base: string | undefined,\n): string | undefined {\n if (!image || /^https?:\\/\\//i.test(image)) return image;\n if (!base) return image;\n try {\n return new URL(image, base).toString();\n } catch {\n return image;\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 // Self-referencing canonical by default.\n //\n // Without this, a route that renders PageHead but omits `url` emits no\n // canonical at all, so the static `<link rel=\"canonical\">` in index.html\n // survives and every such page declares itself a duplicate of the home\n // page — Google then drops /pricing, /privacy, /terms and friends from the\n // index. Deriving it from the current location keeps each page pointing at\n // itself; callers that genuinely need a cross-canonical still pass `url`.\n //\n // During prerender the DOM location already reflects the route being\n // rendered, so this resolves correctly ahead of hydration.\n const resolvedUrl = url ?? canonicalFromLocation();\n\n // Root-relative images (\"/og.png\") belong to the ORIGIN, not to the current\n // path — concatenating them onto the page URL yields /features/og.png and a\n // broken share card. Anything else is resolved relative to the page itself.\n const fullImageUrl = resolveImageUrl(image, resolvedUrl);\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 {resolvedUrl && <link rel=\"canonical\" href={resolvedUrl} />}\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 {resolvedUrl && <meta property=\"og:url\" content={resolvedUrl} />}\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"]}
1
+ {"version":3,"sources":["../../src/seo/page-head.tsx"],"names":["Helmet","jsx"],"mappings":"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","file":"index.js","sourcesContent":["/**\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\n/**\n * Build a self-referencing canonical URL from the current location.\n *\n * Query strings and hashes are dropped (they never identify a distinct\n * canonical document here) and the trailing slash is normalised away for\n * everything but the site root. Returns `undefined` outside a DOM so the\n * component degrades to its previous behaviour under plain string rendering.\n */\nfunction canonicalFromLocation(): string | undefined {\n if (typeof window === \"undefined\" || !window.location) return undefined;\n const { origin, pathname } = window.location;\n if (!origin || origin === \"null\") return undefined;\n const path = pathname === \"/\" ? \"/\" : pathname.replace(/\\/+$/, \"\");\n return `${origin}${path}`;\n}\n\n/**\n * Absolutise an Open Graph image URL.\n *\n * Already-absolute URLs pass through. A root-relative path resolves against\n * the origin, and any other relative path against the page URL. Without a\n * resolvable base the original value is returned unchanged.\n */\nfunction resolveImageUrl(\n image: string | undefined,\n base: string | undefined,\n): string | undefined {\n if (!image || /^https?:\\/\\//i.test(image)) return image;\n if (!base) return image;\n try {\n return new URL(image, base).toString();\n } catch {\n return image;\n }\n}\n\n/**\n * JSON-LD as the body of a `<script>` element.\n *\n * `JSON.stringify` leaves `<` alone, and a script body ends at the first `</script` whatever\n * the quoting around it. The prerender serialises the live DOM with script text verbatim,\n * so a `</script><script>…` inside any string field (a caption, a title) would close the\n * JSON-LD block in the static HTML and run what follows. `\\u003c` is the same character to\n * every JSON parser; U+2028 / U+2029 are escaped for engines that still read them as line\n * breaks.\n */\nfunction serializeJsonLd(data: Record<string, unknown>): string {\n return JSON.stringify(data)\n .replace(/</g, \"\\\\u003c\")\n .replace(/\\u2028/g, \"\\\\u2028\")\n .replace(/\\u2029/g, \"\\\\u2029\");\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 // Self-referencing canonical by default.\n //\n // Without this, a route that renders PageHead but omits `url` emits no\n // canonical at all, so the static `<link rel=\"canonical\">` in index.html\n // survives and every such page declares itself a duplicate of the home\n // page — Google then drops /pricing, /privacy, /terms and friends from the\n // index. Deriving it from the current location keeps each page pointing at\n // itself; callers that genuinely need a cross-canonical still pass `url`.\n //\n // During prerender the DOM location already reflects the route being\n // rendered, so this resolves correctly ahead of hydration.\n const resolvedUrl = url ?? canonicalFromLocation();\n\n // Root-relative images (\"/og.png\") belong to the ORIGIN, not to the current\n // path — concatenating them onto the page URL yields /features/og.png and a\n // broken share card. Anything else is resolved relative to the page itself.\n const fullImageUrl = resolveImageUrl(image, resolvedUrl);\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 {resolvedUrl && <link rel=\"canonical\" href={resolvedUrl} />}\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 {resolvedUrl && <meta property=\"og:url\" content={resolvedUrl} />}\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 {serializeJsonLd(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"]}
@@ -17,6 +17,9 @@ function resolveImageUrl(image, base) {
17
17
  return image;
18
18
  }
19
19
  }
20
+ function serializeJsonLd(data) {
21
+ return JSON.stringify(data).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
22
+ }
20
23
  function PageHead({
21
24
  title,
22
25
  description,
@@ -58,7 +61,7 @@ function PageHead({
58
61
  fullImageUrl && /* @__PURE__ */ jsx("meta", { name: "twitter:image", content: fullImageUrl }),
59
62
  twitterHandle && /* @__PURE__ */ jsx("meta", { name: "twitter:creator", content: twitterHandle }),
60
63
  /* @__PURE__ */ jsx("meta", { name: "theme-color", content: themeColor }),
61
- structuredDataItems.map((data, index) => /* @__PURE__ */ jsx("script", { type: "application/ld+json", children: JSON.stringify(data) }, `ld-${index}`)),
64
+ structuredDataItems.map((data, index) => /* @__PURE__ */ jsx("script", { type: "application/ld+json", children: serializeJsonLd(data) }, `ld-${index}`)),
62
65
  additionalMeta?.map((meta, index) => /* @__PURE__ */ jsx(
63
66
  "meta",
64
67
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/seo/page-head.tsx"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\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\n/**\n * Build a self-referencing canonical URL from the current location.\n *\n * Query strings and hashes are dropped (they never identify a distinct\n * canonical document here) and the trailing slash is normalised away for\n * everything but the site root. Returns `undefined` outside a DOM so the\n * component degrades to its previous behaviour under plain string rendering.\n */\nfunction canonicalFromLocation(): string | undefined {\n if (typeof window === \"undefined\" || !window.location) return undefined;\n const { origin, pathname } = window.location;\n if (!origin || origin === \"null\") return undefined;\n const path = pathname === \"/\" ? \"/\" : pathname.replace(/\\/+$/, \"\");\n return `${origin}${path}`;\n}\n\n/**\n * Absolutise an Open Graph image URL.\n *\n * Already-absolute URLs pass through. A root-relative path resolves against\n * the origin, and any other relative path against the page URL. Without a\n * resolvable base the original value is returned unchanged.\n */\nfunction resolveImageUrl(\n image: string | undefined,\n base: string | undefined,\n): string | undefined {\n if (!image || /^https?:\\/\\//i.test(image)) return image;\n if (!base) return image;\n try {\n return new URL(image, base).toString();\n } catch {\n return image;\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 // Self-referencing canonical by default.\n //\n // Without this, a route that renders PageHead but omits `url` emits no\n // canonical at all, so the static `<link rel=\"canonical\">` in index.html\n // survives and every such page declares itself a duplicate of the home\n // page — Google then drops /pricing, /privacy, /terms and friends from the\n // index. Deriving it from the current location keeps each page pointing at\n // itself; callers that genuinely need a cross-canonical still pass `url`.\n //\n // During prerender the DOM location already reflects the route being\n // rendered, so this resolves correctly ahead of hydration.\n const resolvedUrl = url ?? canonicalFromLocation();\n\n // Root-relative images (\"/og.png\") belong to the ORIGIN, not to the current\n // path — concatenating them onto the page URL yields /features/og.png and a\n // broken share card. Anything else is resolved relative to the page itself.\n const fullImageUrl = resolveImageUrl(image, resolvedUrl);\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 {resolvedUrl && <link rel=\"canonical\" href={resolvedUrl} />}\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 {resolvedUrl && <meta property=\"og:url\" content={resolvedUrl} />}\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"]}
1
+ {"version":3,"sources":["../../src/seo/page-head.tsx"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\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\n/**\n * Build a self-referencing canonical URL from the current location.\n *\n * Query strings and hashes are dropped (they never identify a distinct\n * canonical document here) and the trailing slash is normalised away for\n * everything but the site root. Returns `undefined` outside a DOM so the\n * component degrades to its previous behaviour under plain string rendering.\n */\nfunction canonicalFromLocation(): string | undefined {\n if (typeof window === \"undefined\" || !window.location) return undefined;\n const { origin, pathname } = window.location;\n if (!origin || origin === \"null\") return undefined;\n const path = pathname === \"/\" ? \"/\" : pathname.replace(/\\/+$/, \"\");\n return `${origin}${path}`;\n}\n\n/**\n * Absolutise an Open Graph image URL.\n *\n * Already-absolute URLs pass through. A root-relative path resolves against\n * the origin, and any other relative path against the page URL. Without a\n * resolvable base the original value is returned unchanged.\n */\nfunction resolveImageUrl(\n image: string | undefined,\n base: string | undefined,\n): string | undefined {\n if (!image || /^https?:\\/\\//i.test(image)) return image;\n if (!base) return image;\n try {\n return new URL(image, base).toString();\n } catch {\n return image;\n }\n}\n\n/**\n * JSON-LD as the body of a `<script>` element.\n *\n * `JSON.stringify` leaves `<` alone, and a script body ends at the first `</script` whatever\n * the quoting around it. The prerender serialises the live DOM with script text verbatim,\n * so a `</script><script>…` inside any string field (a caption, a title) would close the\n * JSON-LD block in the static HTML and run what follows. `\\u003c` is the same character to\n * every JSON parser; U+2028 / U+2029 are escaped for engines that still read them as line\n * breaks.\n */\nfunction serializeJsonLd(data: Record<string, unknown>): string {\n return JSON.stringify(data)\n .replace(/</g, \"\\\\u003c\")\n .replace(/\\u2028/g, \"\\\\u2028\")\n .replace(/\\u2029/g, \"\\\\u2029\");\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 // Self-referencing canonical by default.\n //\n // Without this, a route that renders PageHead but omits `url` emits no\n // canonical at all, so the static `<link rel=\"canonical\">` in index.html\n // survives and every such page declares itself a duplicate of the home\n // page — Google then drops /pricing, /privacy, /terms and friends from the\n // index. Deriving it from the current location keeps each page pointing at\n // itself; callers that genuinely need a cross-canonical still pass `url`.\n //\n // During prerender the DOM location already reflects the route being\n // rendered, so this resolves correctly ahead of hydration.\n const resolvedUrl = url ?? canonicalFromLocation();\n\n // Root-relative images (\"/og.png\") belong to the ORIGIN, not to the current\n // path — concatenating them onto the page URL yields /features/og.png and a\n // broken share card. Anything else is resolved relative to the page itself.\n const fullImageUrl = resolveImageUrl(image, resolvedUrl);\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 {resolvedUrl && <link rel=\"canonical\" href={resolvedUrl} />}\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 {resolvedUrl && <meta property=\"og:url\" content={resolvedUrl} />}\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 {serializeJsonLd(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"]}
@@ -467,6 +467,9 @@ function resolveImageUrl(image, base) {
467
467
  return image;
468
468
  }
469
469
  }
470
+ function serializeJsonLd(data) {
471
+ return JSON.stringify(data).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
472
+ }
470
473
  function PageHead({
471
474
  title,
472
475
  description,
@@ -508,7 +511,7 @@ function PageHead({
508
511
  fullImageUrl && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:image", content: fullImageUrl }),
509
512
  twitterHandle && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:creator", content: twitterHandle }),
510
513
  /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "theme-color", content: themeColor }),
511
- structuredDataItems.map((data, index) => /* @__PURE__ */ jsxRuntime.jsx("script", { type: "application/ld+json", children: JSON.stringify(data) }, `ld-${index}`)),
514
+ structuredDataItems.map((data, index) => /* @__PURE__ */ jsxRuntime.jsx("script", { type: "application/ld+json", children: serializeJsonLd(data) }, `ld-${index}`)),
512
515
  additionalMeta?.map((meta, index) => /* @__PURE__ */ jsxRuntime.jsx(
513
516
  "meta",
514
517
  {
@@ -1783,19 +1786,26 @@ function WorkflowEmbed({
1783
1786
  }
1784
1787
  );
1785
1788
  }
1789
+ function MarkdownLink({
1790
+ href,
1791
+ children,
1792
+ // react-markdown passes the hast node to every override; it is not a DOM attribute.
1793
+ node: _node,
1794
+ ...props
1795
+ }) {
1796
+ const isExternal = typeof href === "string" && (href.startsWith("http://") || href.startsWith("https://"));
1797
+ return /* @__PURE__ */ jsxRuntime.jsx(
1798
+ "a",
1799
+ {
1800
+ href,
1801
+ ...props,
1802
+ ...isExternal ? { target: "_blank", rel: "noopener noreferrer" } : { rel: "noopener noreferrer" },
1803
+ children
1804
+ }
1805
+ );
1806
+ }
1786
1807
  var defaultComponents = {
1787
- a: ({ href, children, ...props }) => {
1788
- const isExternal = typeof href === "string" && (href.startsWith("http://") || href.startsWith("https://"));
1789
- return /* @__PURE__ */ jsxRuntime.jsx(
1790
- "a",
1791
- {
1792
- href,
1793
- ...props,
1794
- ...isExternal ? { target: "_blank", rel: "noopener noreferrer" } : { rel: "noopener noreferrer" },
1795
- children
1796
- }
1797
- );
1798
- }
1808
+ a: MarkdownLink
1799
1809
  };
1800
1810
  function Markdown({
1801
1811
  children,