@burdenoff/website-sdk 2026.805.1 → 2026.813.2

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 (41) hide show
  1. package/dist/components/contact.js +20 -3
  2. package/dist/components/contact.js.map +1 -1
  3. package/dist/components/contact.mjs +20 -3
  4. package/dist/components/contact.mjs.map +1 -1
  5. package/dist/components/newsletter.js +20 -3
  6. package/dist/components/newsletter.js.map +1 -1
  7. package/dist/components/newsletter.mjs +20 -3
  8. package/dist/components/newsletter.mjs.map +1 -1
  9. package/dist/components/partners.js +20 -3
  10. package/dist/components/partners.js.map +1 -1
  11. package/dist/components/partners.mjs +20 -3
  12. package/dist/components/partners.mjs.map +1 -1
  13. package/dist/components/pricing.js +20 -3
  14. package/dist/components/pricing.js.map +1 -1
  15. package/dist/components/pricing.mjs +20 -3
  16. package/dist/components/pricing.mjs.map +1 -1
  17. package/dist/components/unsubscribe.js +20 -3
  18. package/dist/components/unsubscribe.js.map +1 -1
  19. package/dist/components/unsubscribe.mjs +20 -3
  20. package/dist/components/unsubscribe.mjs.map +1 -1
  21. package/dist/components/waitlist.js +20 -3
  22. package/dist/components/waitlist.js.map +1 -1
  23. package/dist/components/waitlist.mjs +20 -3
  24. package/dist/components/waitlist.mjs.map +1 -1
  25. package/dist/content/index.js +20 -3
  26. package/dist/content/index.js.map +1 -1
  27. package/dist/content/index.mjs +20 -3
  28. package/dist/content/index.mjs.map +1 -1
  29. package/dist/index.js +27 -10
  30. package/dist/index.js.map +1 -1
  31. package/dist/index.mjs +27 -10
  32. package/dist/index.mjs.map +1 -1
  33. package/dist/seo/index.js +20 -3
  34. package/dist/seo/index.js.map +1 -1
  35. package/dist/seo/index.mjs +20 -3
  36. package/dist/seo/index.mjs.map +1 -1
  37. package/dist/store/index.js +20 -3
  38. package/dist/store/index.js.map +1 -1
  39. package/dist/store/index.mjs +20 -3
  40. package/dist/store/index.mjs.map +1 -1
  41. package/package.json +2 -2
package/dist/seo/index.js CHANGED
@@ -3,6 +3,22 @@
3
3
  var reactHelmetAsync = require('react-helmet-async');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
 
6
+ function canonicalFromLocation() {
7
+ if (typeof window === "undefined" || !window.location) return void 0;
8
+ const { origin, pathname } = window.location;
9
+ if (!origin || origin === "null") return void 0;
10
+ const path = pathname === "/" ? "/" : pathname.replace(/\/+$/, "");
11
+ return `${origin}${path}`;
12
+ }
13
+ function resolveImageUrl(image, base) {
14
+ if (!image || /^https?:\/\//i.test(image)) return image;
15
+ if (!base) return image;
16
+ try {
17
+ return new URL(image, base).toString();
18
+ } catch {
19
+ return image;
20
+ }
21
+ }
6
22
  function PageHead({
7
23
  title,
8
24
  description,
@@ -19,7 +35,8 @@ function PageHead({
19
35
  additionalMeta
20
36
  }) {
21
37
  const fullTitle = productName && !title.includes(productName) ? `${title} | ${productName}` : title;
22
- const fullImageUrl = image && !image.startsWith("http") && url ? `${url.replace(/\/$/, "")}${image}` : image;
38
+ const resolvedUrl = url ?? canonicalFromLocation();
39
+ const fullImageUrl = resolveImageUrl(image, resolvedUrl);
23
40
  const structuredDataItems = structuredData ? Array.isArray(structuredData) ? structuredData : [structuredData] : [];
24
41
  return /* @__PURE__ */ jsxRuntime.jsxs(reactHelmetAsync.Helmet, { children: [
25
42
  /* @__PURE__ */ jsxRuntime.jsx("title", { children: fullTitle }),
@@ -28,13 +45,13 @@ function PageHead({
28
45
  keywords && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "keywords", content: keywords }),
29
46
  /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "author", content: "Burdenoff Consultancy Services Pvt Ltd" }),
30
47
  /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "robots", content: robots }),
31
- url && /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "canonical", href: url }),
48
+ resolvedUrl && /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "canonical", href: resolvedUrl }),
32
49
  favicon && /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "icon", href: favicon }),
33
50
  /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:type", content: "website" }),
34
51
  /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:title", content: fullTitle }),
35
52
  description && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:description", content: description }),
36
53
  fullImageUrl && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:image", content: fullImageUrl }),
37
- url && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:url", content: url }),
54
+ resolvedUrl && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:url", content: resolvedUrl }),
38
55
  /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:locale", content: locale }),
39
56
  productName && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:site_name", content: productName }),
40
57
  /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:card", content: "summary_large_image" }),
@@ -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\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"]}
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,6 +1,22 @@
1
1
  import { Helmet } from 'react-helmet-async';
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
3
 
4
+ function canonicalFromLocation() {
5
+ if (typeof window === "undefined" || !window.location) return void 0;
6
+ const { origin, pathname } = window.location;
7
+ if (!origin || origin === "null") return void 0;
8
+ const path = pathname === "/" ? "/" : pathname.replace(/\/+$/, "");
9
+ return `${origin}${path}`;
10
+ }
11
+ function resolveImageUrl(image, base) {
12
+ if (!image || /^https?:\/\//i.test(image)) return image;
13
+ if (!base) return image;
14
+ try {
15
+ return new URL(image, base).toString();
16
+ } catch {
17
+ return image;
18
+ }
19
+ }
4
20
  function PageHead({
5
21
  title,
6
22
  description,
@@ -17,7 +33,8 @@ function PageHead({
17
33
  additionalMeta
18
34
  }) {
19
35
  const fullTitle = productName && !title.includes(productName) ? `${title} | ${productName}` : title;
20
- const fullImageUrl = image && !image.startsWith("http") && url ? `${url.replace(/\/$/, "")}${image}` : image;
36
+ const resolvedUrl = url ?? canonicalFromLocation();
37
+ const fullImageUrl = resolveImageUrl(image, resolvedUrl);
21
38
  const structuredDataItems = structuredData ? Array.isArray(structuredData) ? structuredData : [structuredData] : [];
22
39
  return /* @__PURE__ */ jsxs(Helmet, { children: [
23
40
  /* @__PURE__ */ jsx("title", { children: fullTitle }),
@@ -26,13 +43,13 @@ function PageHead({
26
43
  keywords && /* @__PURE__ */ jsx("meta", { name: "keywords", content: keywords }),
27
44
  /* @__PURE__ */ jsx("meta", { name: "author", content: "Burdenoff Consultancy Services Pvt Ltd" }),
28
45
  /* @__PURE__ */ jsx("meta", { name: "robots", content: robots }),
29
- url && /* @__PURE__ */ jsx("link", { rel: "canonical", href: url }),
46
+ resolvedUrl && /* @__PURE__ */ jsx("link", { rel: "canonical", href: resolvedUrl }),
30
47
  favicon && /* @__PURE__ */ jsx("link", { rel: "icon", href: favicon }),
31
48
  /* @__PURE__ */ jsx("meta", { property: "og:type", content: "website" }),
32
49
  /* @__PURE__ */ jsx("meta", { property: "og:title", content: fullTitle }),
33
50
  description && /* @__PURE__ */ jsx("meta", { property: "og:description", content: description }),
34
51
  fullImageUrl && /* @__PURE__ */ jsx("meta", { property: "og:image", content: fullImageUrl }),
35
- url && /* @__PURE__ */ jsx("meta", { property: "og:url", content: url }),
52
+ resolvedUrl && /* @__PURE__ */ jsx("meta", { property: "og:url", content: resolvedUrl }),
36
53
  /* @__PURE__ */ jsx("meta", { property: "og:locale", content: locale }),
37
54
  productName && /* @__PURE__ */ jsx("meta", { property: "og:site_name", content: productName }),
38
55
  /* @__PURE__ */ jsx("meta", { name: "twitter:card", content: "summary_large_image" }),
@@ -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\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"]}
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"]}
@@ -424,6 +424,22 @@ function useWebSDK() {
424
424
  }
425
425
  return client;
426
426
  }
427
+ function canonicalFromLocation() {
428
+ if (typeof window === "undefined" || !window.location) return void 0;
429
+ const { origin, pathname } = window.location;
430
+ if (!origin || origin === "null") return void 0;
431
+ const path = pathname === "/" ? "/" : pathname.replace(/\/+$/, "");
432
+ return `${origin}${path}`;
433
+ }
434
+ function resolveImageUrl(image, base) {
435
+ if (!image || /^https?:\/\//i.test(image)) return image;
436
+ if (!base) return image;
437
+ try {
438
+ return new URL(image, base).toString();
439
+ } catch {
440
+ return image;
441
+ }
442
+ }
427
443
  function PageHead({
428
444
  title,
429
445
  description,
@@ -440,7 +456,8 @@ function PageHead({
440
456
  additionalMeta
441
457
  }) {
442
458
  const fullTitle = productName && !title.includes(productName) ? `${title} | ${productName}` : title;
443
- const fullImageUrl = image && !image.startsWith("http") && url ? `${url.replace(/\/$/, "")}${image}` : image;
459
+ const resolvedUrl = url ?? canonicalFromLocation();
460
+ const fullImageUrl = resolveImageUrl(image, resolvedUrl);
444
461
  const structuredDataItems = structuredData ? Array.isArray(structuredData) ? structuredData : [structuredData] : [];
445
462
  return /* @__PURE__ */ jsxRuntime.jsxs(reactHelmetAsync.Helmet, { children: [
446
463
  /* @__PURE__ */ jsxRuntime.jsx("title", { children: fullTitle }),
@@ -449,13 +466,13 @@ function PageHead({
449
466
  keywords && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "keywords", content: keywords }),
450
467
  /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "author", content: "Burdenoff Consultancy Services Pvt Ltd" }),
451
468
  /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "robots", content: robots }),
452
- url && /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "canonical", href: url }),
469
+ resolvedUrl && /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "canonical", href: resolvedUrl }),
453
470
  favicon && /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "icon", href: favicon }),
454
471
  /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:type", content: "website" }),
455
472
  /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:title", content: fullTitle }),
456
473
  description && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:description", content: description }),
457
474
  fullImageUrl && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:image", content: fullImageUrl }),
458
- url && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:url", content: url }),
475
+ resolvedUrl && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:url", content: resolvedUrl }),
459
476
  /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:locale", content: locale }),
460
477
  productName && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:site_name", content: productName }),
461
478
  /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:card", content: "summary_large_image" }),