@salesmind-ai/design-system 0.1.7 → 0.1.9

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.
@@ -1,5 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode } from 'react';
3
+ import { U as UtmParams } from '../audit-CiyPkxk1.cjs';
4
+ export { F as FirstTouchAttribution, a as UTM_CAMPAIGNS, b as UTM_CONTENTS, c as UTM_MEDIUMS_ALL, d as UTM_MEDIUMS_APP, e as UTM_MEDIUMS_MESSAGING, f as UTM_MEDIUMS_WEB, g as UTM_SOURCES, h as UTM_SOURCES_REQUIRING_SELLER, i as UTM_TERMS, j as UrlClassification, k as UtmAuditEntry, l as UtmBlockedError, m as UtmCampaign, n as UtmComplianceResult, o as UtmComplianceStatus, p as UtmConfidence, q as UtmContent, r as UtmMedium, s as UtmMediumAppPage, t as UtmMediumMessaging, u as UtmMediumWebPage, v as UtmOverrides, w as UtmSource, x as UtmSourceRequiringSeller, y as UtmTerm, z as buildBlockedError, A as buildUtmUrl, B as classifyAndEnforce, C as classifyUrl, D as createAuditEntry, E as isUtmExempt, G as isValidUtmParams, H as parseUtmParams, I as requiresSellerAttribution, J as requiresUtm, K as toFirstTouchAttribution, L as validateCompliance, M as validateUtmField } from '../audit-CiyPkxk1.cjs';
3
5
 
4
6
  /**
5
7
  * Schema.org SEO Generator Types
@@ -407,7 +409,7 @@ declare function createSchemaGenerators(brand: BrandConfig, supportedLanguages?:
407
409
  inLanguage: string;
408
410
  worksFor: {
409
411
  name: string;
410
- '@id'?: string;
412
+ '@id'?: string | undefined;
411
413
  } | {
412
414
  '@type': string;
413
415
  '@id': `${string}/#organization`;
@@ -887,4 +889,28 @@ declare function AnalyticsProvider({ onTrack, debug, children }: AnalyticsProvid
887
889
  */
888
890
  declare function useAnalytics(): AnalyticsContextValue;
889
891
 
890
- export { type AnalyticsContextValue, type AnalyticsEventProps, type AnalyticsLoaderConfig, AnalyticsProvider, type AnalyticsProviderProps, type ArticleMeta, type BrandConfig, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, type CareerForSchema, type ConsentStatus, CookieConsent, type CookieConsentLabels, type CookieConsentProps, type CustomerOrganizationMeta, type FAQItem, type GlossaryTermForSchema, type HowToMeta, type HowToStep, type ItemForList, JsonLd, type JsonLdProps, type PersonMeta, type PricingPlan, type BreadcrumbItem as SeoBreadcrumbItem, type ServiceMotion, type TestimonialForSchema, type TrackFn, type TranscriptForSchema, type VideoObjectMeta, aggregateRatingFromTestimonials, buildPageGraph, canonicalUrl, createAnalyticsLoader, createEntityIds, createSchemaGenerators, loadClarity, loadGoogleAnalytics, useAnalytics, useCookieConsent };
892
+ interface UtmProviderProps {
893
+ /** Default UTM parameters for all descendants. */
894
+ params: UtmParams;
895
+ children: ReactNode;
896
+ }
897
+ /**
898
+ * Provides governed UTM defaults to all `OutboundLink` and UTM-aware
899
+ * components in the subtree.
900
+ *
901
+ * @example
902
+ * ```tsx
903
+ * <UtmProvider params={{ source: 'website', medium: 'webBlogPost', campaign: 'demo' }}>
904
+ * <BlogPostPage />
905
+ * </UtmProvider>
906
+ * ```
907
+ */
908
+ declare function UtmProvider({ params, children }: UtmProviderProps): react_jsx_runtime.JSX.Element;
909
+
910
+ /**
911
+ * Read the nearest UtmProvider's default params.
912
+ * Returns `null` when no provider is present (callers should fall back gracefully).
913
+ */
914
+ declare function useUtmDefaults(): UtmParams | null;
915
+
916
+ export { type AnalyticsContextValue, type AnalyticsEventProps, type AnalyticsLoaderConfig, AnalyticsProvider, type AnalyticsProviderProps, type ArticleMeta, type BrandConfig, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, type CareerForSchema, type ConsentStatus, CookieConsent, type CookieConsentLabels, type CookieConsentProps, type CustomerOrganizationMeta, type FAQItem, type GlossaryTermForSchema, type HowToMeta, type HowToStep, type ItemForList, JsonLd, type JsonLdProps, type PersonMeta, type PricingPlan, type BreadcrumbItem as SeoBreadcrumbItem, type ServiceMotion, type TestimonialForSchema, type TrackFn, type TranscriptForSchema, UtmParams, UtmProvider, type UtmProviderProps, type VideoObjectMeta, aggregateRatingFromTestimonials, buildPageGraph, canonicalUrl, createAnalyticsLoader, createEntityIds, createSchemaGenerators, loadClarity, loadGoogleAnalytics, useAnalytics, useCookieConsent, useUtmDefaults };
@@ -1,5 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode } from 'react';
3
+ import { U as UtmParams } from '../audit-CiyPkxk1.js';
4
+ export { F as FirstTouchAttribution, a as UTM_CAMPAIGNS, b as UTM_CONTENTS, c as UTM_MEDIUMS_ALL, d as UTM_MEDIUMS_APP, e as UTM_MEDIUMS_MESSAGING, f as UTM_MEDIUMS_WEB, g as UTM_SOURCES, h as UTM_SOURCES_REQUIRING_SELLER, i as UTM_TERMS, j as UrlClassification, k as UtmAuditEntry, l as UtmBlockedError, m as UtmCampaign, n as UtmComplianceResult, o as UtmComplianceStatus, p as UtmConfidence, q as UtmContent, r as UtmMedium, s as UtmMediumAppPage, t as UtmMediumMessaging, u as UtmMediumWebPage, v as UtmOverrides, w as UtmSource, x as UtmSourceRequiringSeller, y as UtmTerm, z as buildBlockedError, A as buildUtmUrl, B as classifyAndEnforce, C as classifyUrl, D as createAuditEntry, E as isUtmExempt, G as isValidUtmParams, H as parseUtmParams, I as requiresSellerAttribution, J as requiresUtm, K as toFirstTouchAttribution, L as validateCompliance, M as validateUtmField } from '../audit-CiyPkxk1.js';
3
5
 
4
6
  /**
5
7
  * Schema.org SEO Generator Types
@@ -407,7 +409,7 @@ declare function createSchemaGenerators(brand: BrandConfig, supportedLanguages?:
407
409
  inLanguage: string;
408
410
  worksFor: {
409
411
  name: string;
410
- '@id'?: string;
412
+ '@id'?: string | undefined;
411
413
  } | {
412
414
  '@type': string;
413
415
  '@id': `${string}/#organization`;
@@ -887,4 +889,28 @@ declare function AnalyticsProvider({ onTrack, debug, children }: AnalyticsProvid
887
889
  */
888
890
  declare function useAnalytics(): AnalyticsContextValue;
889
891
 
890
- export { type AnalyticsContextValue, type AnalyticsEventProps, type AnalyticsLoaderConfig, AnalyticsProvider, type AnalyticsProviderProps, type ArticleMeta, type BrandConfig, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, type CareerForSchema, type ConsentStatus, CookieConsent, type CookieConsentLabels, type CookieConsentProps, type CustomerOrganizationMeta, type FAQItem, type GlossaryTermForSchema, type HowToMeta, type HowToStep, type ItemForList, JsonLd, type JsonLdProps, type PersonMeta, type PricingPlan, type BreadcrumbItem as SeoBreadcrumbItem, type ServiceMotion, type TestimonialForSchema, type TrackFn, type TranscriptForSchema, type VideoObjectMeta, aggregateRatingFromTestimonials, buildPageGraph, canonicalUrl, createAnalyticsLoader, createEntityIds, createSchemaGenerators, loadClarity, loadGoogleAnalytics, useAnalytics, useCookieConsent };
892
+ interface UtmProviderProps {
893
+ /** Default UTM parameters for all descendants. */
894
+ params: UtmParams;
895
+ children: ReactNode;
896
+ }
897
+ /**
898
+ * Provides governed UTM defaults to all `OutboundLink` and UTM-aware
899
+ * components in the subtree.
900
+ *
901
+ * @example
902
+ * ```tsx
903
+ * <UtmProvider params={{ source: 'website', medium: 'webBlogPost', campaign: 'demo' }}>
904
+ * <BlogPostPage />
905
+ * </UtmProvider>
906
+ * ```
907
+ */
908
+ declare function UtmProvider({ params, children }: UtmProviderProps): react_jsx_runtime.JSX.Element;
909
+
910
+ /**
911
+ * Read the nearest UtmProvider's default params.
912
+ * Returns `null` when no provider is present (callers should fall back gracefully).
913
+ */
914
+ declare function useUtmDefaults(): UtmParams | null;
915
+
916
+ export { type AnalyticsContextValue, type AnalyticsEventProps, type AnalyticsLoaderConfig, AnalyticsProvider, type AnalyticsProviderProps, type ArticleMeta, type BrandConfig, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, type CareerForSchema, type ConsentStatus, CookieConsent, type CookieConsentLabels, type CookieConsentProps, type CustomerOrganizationMeta, type FAQItem, type GlossaryTermForSchema, type HowToMeta, type HowToStep, type ItemForList, JsonLd, type JsonLdProps, type PersonMeta, type PricingPlan, type BreadcrumbItem as SeoBreadcrumbItem, type ServiceMotion, type TestimonialForSchema, type TrackFn, type TranscriptForSchema, UtmParams, UtmProvider, type UtmProviderProps, type VideoObjectMeta, aggregateRatingFromTestimonials, buildPageGraph, canonicalUrl, createAnalyticsLoader, createEntityIds, createSchemaGenerators, loadClarity, loadGoogleAnalytics, useAnalytics, useCookieConsent, useUtmDefaults };
package/dist/web/index.js CHANGED
@@ -1,5 +1,6 @@
1
+ "use client";
1
2
  import { jsx, jsxs } from 'react/jsx-runtime';
2
- import React, { forwardRef, useState, useEffect, createContext, useCallback, useMemo, useContext } from 'react';
3
+ import React, { createContext, forwardRef, useState, useEffect, useContext, useCallback, useMemo } from 'react';
3
4
  import { Slot } from '@radix-ui/react-slot';
4
5
  import clsx from 'clsx';
5
6
  import { AnimatePresence, motion } from 'framer-motion';
@@ -649,23 +650,146 @@ var Button = React.forwardRef(
649
650
  }
650
651
  );
651
652
  Button.displayName = "Button";
653
+ var UtmContext = createContext(null);
654
+
655
+ // src/web/utm/useUtmDefaults.ts
656
+ function useUtmDefaults() {
657
+ return useContext(UtmContext);
658
+ }
659
+
660
+ // src/web/utm/builders.ts
661
+ var PLACEHOLDER_ORIGIN = "https://__placeholder__.internal";
662
+ function buildUtmUrl(baseUrl, params) {
663
+ const isRelative = baseUrl.startsWith("/");
664
+ let url;
665
+ try {
666
+ url = isRelative ? new URL(baseUrl, PLACEHOLDER_ORIGIN) : new URL(baseUrl);
667
+ } catch {
668
+ return baseUrl;
669
+ }
670
+ const existingParams = [];
671
+ url.searchParams.forEach((value, key) => {
672
+ existingParams.push([key, value]);
673
+ });
674
+ for (const [key] of existingParams) {
675
+ url.searchParams.delete(key);
676
+ }
677
+ for (const [key, value] of existingParams) {
678
+ if (!key.startsWith("utm_")) {
679
+ url.searchParams.set(key, value);
680
+ }
681
+ }
682
+ url.searchParams.set("utm_source", params.source);
683
+ url.searchParams.set("utm_medium", params.medium);
684
+ url.searchParams.set("utm_campaign", params.campaign);
685
+ if (params.term !== void 0) {
686
+ url.searchParams.set("utm_term", params.term);
687
+ }
688
+ if (params.content !== void 0) {
689
+ url.searchParams.set("utm_content", params.content);
690
+ }
691
+ if (isRelative) {
692
+ return url.href.replace(PLACEHOLDER_ORIGIN, "");
693
+ }
694
+ return url.href;
695
+ }
696
+
697
+ // src/web/utm/classifiers.ts
698
+ var INTERNAL_PATTERNS = [
699
+ /^\/(?!\/)/,
700
+ // Relative paths
701
+ /^https?:\/\/(www\.)?sales-mind\.ai/i,
702
+ // Marketing site
703
+ /^https?:\/\/app\.sales-mind\.ai/i,
704
+ // Web app
705
+ /^https?:\/\/apps\.sales-mind\.ai/i,
706
+ // Web app (legacy)
707
+ /^https?:\/\/meet\.sales-mind\.ai/i,
708
+ // Booking
709
+ /^https?:\/\/docs\.sales-mind\.ai/i
710
+ // Docs
711
+ ];
712
+ var SYSTEM_PATTERNS = [
713
+ /^https?:\/\/.*\/api\//i,
714
+ // API endpoints
715
+ /^https?:\/\/.*\/webhook/i,
716
+ // Webhooks
717
+ /^https?:\/\/.*\/oauth/i,
718
+ // OAuth callbacks
719
+ /^https?:\/\/.*\/callback/i,
720
+ // Callbacks
721
+ /^https?:\/\/.*\.supabase\.(co|com)/i,
722
+ // Supabase
723
+ /^https?:\/\/.*\.firebaseapp\.com/i,
724
+ // Firebase
725
+ /^https?:\/\/.*\.cloudfunctions\.net/i
726
+ // Cloud Functions
727
+ ];
728
+ var ASSET_PATTERNS = [
729
+ /\.(css|js|mjs|map|woff2?|ttf|eot|svg|png|jpe?g|gif|webp|avif|ico|pdf)(\?.*)?$/i
730
+ ];
731
+ var CONVERSION_PATTERNS = [
732
+ /^https?:\/\/(www\.)?calendly\.com/i,
733
+ /^https?:\/\/(checkout\.)?stripe\.com/i,
734
+ /^https?:\/\/buy\.stripe\.com/i,
735
+ /^https?:\/\/chromewebstore\.google\.com/i,
736
+ /^https?:\/\/meet\.sales-mind\.ai/i
737
+ ];
738
+ var PROTOCOL_EXEMPT = [
739
+ /^mailto:/i,
740
+ /^tel:/i,
741
+ /^#/,
742
+ /^javascript:/i
743
+ ];
744
+ function classifyUrl(url) {
745
+ if (PROTOCOL_EXEMPT.some((p) => p.test(url))) return "protocol";
746
+ if (ASSET_PATTERNS.some((p) => p.test(url))) return "asset";
747
+ if (SYSTEM_PATTERNS.some((p) => p.test(url))) return "system";
748
+ if (CONVERSION_PATTERNS.some((p) => p.test(url))) return "conversion";
749
+ if (INTERNAL_PATTERNS.some((p) => p.test(url))) return "internal";
750
+ return "external";
751
+ }
752
+ function requiresUtm(url) {
753
+ const classification = classifyUrl(url);
754
+ return classification === "external" || classification === "conversion";
755
+ }
756
+ function isUtmExempt(url) {
757
+ const classification = classifyUrl(url);
758
+ return classification === "protocol" || classification === "asset" || classification === "system" || classification === "internal";
759
+ }
652
760
 
653
761
  // src/components/OutboundLink/outbound-link-utils.ts
654
- var CONSTANT_UTM_SOURCE = "salesmind";
762
+ var LEGACY_UTM_SOURCE = "salesmind";
655
763
  var EXEMPT_PATTERNS = [
656
764
  /^https?:\/\/(www\.)?(stripe\.com|checkout\.stripe\.com|paypal\.com)/i,
657
765
  /^https?:\/\/(www\.)?github\.com\/login\/oauth/i
658
766
  ];
659
767
  var isExemptUrl = (urlStr) => {
660
768
  if (urlStr.startsWith("mailto:") || urlStr.startsWith("tel:")) return true;
769
+ const classification = classifyUrl(urlStr);
770
+ if (classification === "system" || classification === "protocol" || classification === "asset") {
771
+ return true;
772
+ }
661
773
  return EXEMPT_PATTERNS.some((pattern) => pattern.test(urlStr));
662
774
  };
775
+ var appendGovernedUTMs = (href, params, preserveExisting = true) => {
776
+ try {
777
+ const url = new URL(href);
778
+ if (preserveExisting) {
779
+ const hasAll = url.searchParams.has("utm_source") && url.searchParams.has("utm_medium") && url.searchParams.has("utm_campaign");
780
+ if (hasAll) return href;
781
+ }
782
+ return buildUtmUrl(href, params);
783
+ } catch {
784
+ return href;
785
+ }
786
+ };
663
787
  var appendUTMs = (href, context, pageSlug, options) => {
664
788
  try {
665
789
  const url = new URL(href);
666
790
  const { mediumOverride = "outbound_link", campaignOverride, preserveExisting = true } = options;
667
791
  const utms = {
668
- utm_source: CONSTANT_UTM_SOURCE,
792
+ utm_source: LEGACY_UTM_SOURCE,
669
793
  utm_medium: mediumOverride,
670
794
  utm_campaign: campaignOverride || pageSlug,
671
795
  utm_content: context
@@ -692,10 +816,13 @@ var OutboundLink = forwardRef(
692
816
  preserveExistingUTM = true,
693
817
  openInNewTab = true,
694
818
  disableTracking = false,
819
+ utmParams,
695
820
  onClick,
696
821
  children,
697
822
  ...props
698
823
  }, ref) => {
824
+ const contextParams = useUtmDefaults();
825
+ const resolvedUtmParams = utmParams ?? contextParams;
699
826
  let hostname = "";
700
827
  try {
701
828
  const url = new URL(href);
@@ -720,16 +847,20 @@ var OutboundLink = forwardRef(
720
847
  }
721
848
  const isExempt = isExemptUrl(href) || disableTracking;
722
849
  if (isExternal && !isExempt) {
723
- const pageSlug = window.location.pathname.replace(/^\/|\/$/g, "") || "home";
724
- setFinalHref(appendUTMs(href, context, pageSlug, {
725
- mediumOverride: currentMedium,
726
- campaignOverride,
727
- preserveExisting: preserveExistingUTM
728
- }));
850
+ if (resolvedUtmParams) {
851
+ setFinalHref(appendGovernedUTMs(href, resolvedUtmParams, preserveExistingUTM));
852
+ } else {
853
+ const pageSlug = window.location.pathname.replace(/^\/|\/$/g, "") || "home";
854
+ setFinalHref(appendUTMs(href, context, pageSlug, {
855
+ mediumOverride: currentMedium,
856
+ campaignOverride,
857
+ preserveExisting: preserveExistingUTM
858
+ }));
859
+ }
729
860
  } else {
730
861
  setFinalHref(href);
731
862
  }
732
- }, [href, context, mediumOverride, campaignOverride, preserveExistingUTM, disableTracking]);
863
+ }, [href, context, mediumOverride, campaignOverride, preserveExistingUTM, disableTracking, resolvedUtmParams]);
733
864
  const handleClick = (e) => {
734
865
  if (typeof window === "undefined" || disableTracking) {
735
866
  onClick?.(e);
@@ -902,6 +1033,268 @@ function useAnalytics() {
902
1033
  return useContext(AnalyticsContext) ?? NOOP_VALUE;
903
1034
  }
904
1035
 
905
- export { AnalyticsProvider, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, CookieConsent, JsonLd, aggregateRatingFromTestimonials, buildPageGraph, canonicalUrl, createAnalyticsLoader, createEntityIds, createSchemaGenerators, loadClarity, loadGoogleAnalytics, useAnalytics, useCookieConsent };
906
- //# sourceMappingURL=index.js.map
1036
+ // src/web/utm/constants.ts
1037
+ var UTM_SOURCES = [
1038
+ "linkedin",
1039
+ "whatsapp",
1040
+ "intercom",
1041
+ "email",
1042
+ "website",
1043
+ "app",
1044
+ "chromeStore",
1045
+ "stripe",
1046
+ "direct"
1047
+ ];
1048
+ var UTM_MEDIUMS_MESSAGING = [
1049
+ "dm",
1050
+ "group",
1051
+ "email",
1052
+ "inAppChat",
1053
+ "organicPost",
1054
+ "paidAd",
1055
+ "qrCode",
1056
+ "redirect",
1057
+ "storeListing"
1058
+ ];
1059
+ var UTM_MEDIUMS_APP = [
1060
+ "appHome",
1061
+ "appDashboard",
1062
+ "appInbox",
1063
+ "appContacts",
1064
+ "appCampaigns",
1065
+ "appSettings",
1066
+ "appBilling",
1067
+ "appCheckout",
1068
+ "appOnboarding",
1069
+ "appSupport",
1070
+ "appCalendar"
1071
+ ];
1072
+ var UTM_MEDIUMS_WEB = [
1073
+ "webHome",
1074
+ "webDemo",
1075
+ "webPricing",
1076
+ "webFeatures",
1077
+ "webUseCase",
1078
+ "webSolution",
1079
+ "webIndustry",
1080
+ "webIntegrations",
1081
+ "webBlog",
1082
+ "webBlogPost",
1083
+ "webLanding",
1084
+ "webComparison",
1085
+ "webCaseStudy",
1086
+ "webAbout",
1087
+ "webContact",
1088
+ "webCareers",
1089
+ "webLegal",
1090
+ "webDocs",
1091
+ "webAffiliate"
1092
+ ];
1093
+ var UTM_MEDIUMS_ALL = [
1094
+ ...UTM_MEDIUMS_MESSAGING,
1095
+ ...UTM_MEDIUMS_APP,
1096
+ ...UTM_MEDIUMS_WEB
1097
+ ];
1098
+ var UTM_CAMPAIGNS = [
1099
+ "discoveryCall",
1100
+ "demo",
1101
+ "trial",
1102
+ "onboarding",
1103
+ "upgrade",
1104
+ "renewal",
1105
+ "retention",
1106
+ "accountSupport",
1107
+ "supportCall",
1108
+ "partnerCall",
1109
+ "hiring",
1110
+ "interviewCall"
1111
+ ];
1112
+ var UTM_TERMS = [
1113
+ "julienGadea",
1114
+ "bramSmith",
1115
+ "florentDupont",
1116
+ "sawLin",
1117
+ "evaSupport",
1118
+ "team",
1119
+ "auto"
1120
+ ];
1121
+ var UTM_CONTENTS = [
1122
+ "ctaPrimary",
1123
+ "ctaSecondary",
1124
+ "ctaHeader",
1125
+ "ctaFooter",
1126
+ "ctaInline",
1127
+ "buttonPrimary",
1128
+ "buttonSecondary",
1129
+ "banner",
1130
+ "popup",
1131
+ "variantA",
1132
+ "variantB",
1133
+ "variantC"
1134
+ ];
1135
+ var UTM_SOURCES_REQUIRING_SELLER = [
1136
+ "linkedin",
1137
+ "whatsapp",
1138
+ "intercom",
1139
+ "email"
1140
+ ];
1141
+
1142
+ // src/web/utm/validators.ts
1143
+ function validateUtmField(field, value) {
1144
+ switch (field) {
1145
+ case "source":
1146
+ return UTM_SOURCES.includes(value);
1147
+ case "medium":
1148
+ return UTM_MEDIUMS_ALL.includes(value);
1149
+ case "campaign":
1150
+ return UTM_CAMPAIGNS.includes(value);
1151
+ case "term":
1152
+ return UTM_TERMS.includes(value);
1153
+ case "content":
1154
+ return UTM_CONTENTS.includes(value);
1155
+ default:
1156
+ return false;
1157
+ }
1158
+ }
1159
+ function isValidUtmParams(params) {
1160
+ if (!params.source || !params.medium || !params.campaign) {
1161
+ return false;
1162
+ }
1163
+ if (!validateUtmField("source", params.source)) return false;
1164
+ if (!validateUtmField("medium", params.medium)) return false;
1165
+ if (!validateUtmField("campaign", params.campaign)) return false;
1166
+ if (params.term !== void 0 && !validateUtmField("term", params.term)) return false;
1167
+ if (params.content !== void 0 && !validateUtmField("content", params.content)) return false;
1168
+ return true;
1169
+ }
1170
+ function validateCompliance(url, params) {
1171
+ const errors = [];
1172
+ const classification = classifyUrl(url);
1173
+ const needsUtm = requiresUtm(url);
1174
+ if (!needsUtm) {
1175
+ return {
1176
+ status: "compliant",
1177
+ url,
1178
+ params: params ?? null,
1179
+ errors: []
1180
+ };
1181
+ }
1182
+ if (!params) {
1183
+ return {
1184
+ status: "blocked",
1185
+ url,
1186
+ params: null,
1187
+ errors: [`URL classified as '${classification}' requires UTM parameters but none provided`]
1188
+ };
1189
+ }
1190
+ if (!params.source) errors.push("Missing required field: utm_source");
1191
+ if (!params.medium) errors.push("Missing required field: utm_medium");
1192
+ if (!params.campaign) errors.push("Missing required field: utm_campaign");
1193
+ if (params.source && !validateUtmField("source", params.source)) {
1194
+ errors.push(`Invalid utm_source: '${params.source}'. Must be one of: ${UTM_SOURCES.join(", ")}`);
1195
+ }
1196
+ if (params.medium && !validateUtmField("medium", params.medium)) {
1197
+ errors.push(`Invalid utm_medium: '${params.medium}'. Must be one of governed enum values`);
1198
+ }
1199
+ if (params.campaign && !validateUtmField("campaign", params.campaign)) {
1200
+ errors.push(`Invalid utm_campaign: '${params.campaign}'. Must be one of: ${UTM_CAMPAIGNS.join(", ")}`);
1201
+ }
1202
+ if (params.term !== void 0 && params.term && !validateUtmField("term", params.term)) {
1203
+ errors.push(`Invalid utm_term: '${params.term}'. Must be one of: ${UTM_TERMS.join(", ")}`);
1204
+ }
1205
+ if (params.content !== void 0 && params.content && !validateUtmField("content", params.content)) {
1206
+ errors.push(`Invalid utm_content: '${params.content}'. Must be one of: ${UTM_CONTENTS.join(", ")}`);
1207
+ }
1208
+ const allValues = [];
1209
+ if (params.source) allValues.push(params.source);
1210
+ if (params.medium) allValues.push(params.medium);
1211
+ if (params.campaign) allValues.push(params.campaign);
1212
+ if (params.term) allValues.push(params.term);
1213
+ if (params.content) allValues.push(params.content);
1214
+ for (const value of allValues) {
1215
+ if (/\s/.test(value)) errors.push(`Value '${value}' contains spaces`);
1216
+ if (/_/.test(value)) errors.push(`Value '${value}' contains underscores`);
1217
+ if (/-/.test(value)) errors.push(`Value '${value}' contains hyphens`);
1218
+ if (/[^\x20-\x7E]/.test(value)) errors.push(`Value '${value}' contains non-ASCII characters`);
1219
+ if (value === value.toUpperCase() && value.length > 1) errors.push(`Value '${value}' is ALL CAPS`);
1220
+ }
1221
+ const status = errors.length === 0 ? "compliant" : "blocked";
1222
+ return { status, url, params, errors };
1223
+ }
1224
+ function classifyAndEnforce(url, params) {
1225
+ return validateCompliance(url, params);
1226
+ }
1227
+ function buildBlockedError(reason, url) {
1228
+ return {
1229
+ status: "UTM_COMPLIANCE_BLOCKED",
1230
+ reason,
1231
+ requiredFix: reason,
1232
+ correctedExample: url ?? null
1233
+ };
1234
+ }
1235
+
1236
+ // src/web/utm/attribution.ts
1237
+ function parseUtmParams(search) {
1238
+ if (!search) return {};
1239
+ const params = new URLSearchParams(search);
1240
+ const result = {};
1241
+ const source = params.get("utm_source");
1242
+ if (source && UTM_SOURCES.includes(source)) {
1243
+ result.source = source;
1244
+ }
1245
+ const medium = params.get("utm_medium");
1246
+ if (medium) {
1247
+ result.medium = medium;
1248
+ }
1249
+ const campaign = params.get("utm_campaign");
1250
+ if (campaign && UTM_CAMPAIGNS.includes(campaign)) {
1251
+ result.campaign = campaign;
1252
+ }
1253
+ const term = params.get("utm_term");
1254
+ if (term && UTM_TERMS.includes(term)) {
1255
+ result.term = term;
1256
+ }
1257
+ const content = params.get("utm_content");
1258
+ if (content && UTM_CONTENTS.includes(content)) {
1259
+ result.content = content;
1260
+ }
1261
+ return result;
1262
+ }
1263
+ function toFirstTouchAttribution(params) {
1264
+ return {
1265
+ firstTouchSource: params.source,
1266
+ firstTouchMedium: params.medium,
1267
+ firstTouchCampaign: params.campaign,
1268
+ firstTouchSeller: params.term ?? null
1269
+ };
1270
+ }
1271
+ function requiresSellerAttribution(source) {
1272
+ return UTM_SOURCES_REQUIRING_SELLER.includes(source);
1273
+ }
1274
+
1275
+ // src/web/utm/audit.ts
1276
+ function createAuditEntry(url, params, generatorContext, confidence) {
1277
+ const utmParts = [];
1278
+ if (params) {
1279
+ if (params.source) utmParts.push(`utm_source=${params.source}`);
1280
+ if (params.medium) utmParts.push(`utm_medium=${params.medium}`);
1281
+ if (params.campaign) utmParts.push(`utm_campaign=${params.campaign}`);
1282
+ if (params.term) utmParts.push(`utm_term=${params.term}`);
1283
+ if (params.content) utmParts.push(`utm_content=${params.content}`);
1284
+ }
1285
+ return {
1286
+ url,
1287
+ utmString: utmParts.join("&"),
1288
+ generatorContext,
1289
+ sellerAttribution: params?.term ?? null,
1290
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1291
+ confidence
1292
+ };
1293
+ }
1294
+ function UtmProvider({ params, children }) {
1295
+ return /* @__PURE__ */ jsx(UtmContext.Provider, { value: params, children });
1296
+ }
1297
+
1298
+ export { AnalyticsProvider, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, CookieConsent, JsonLd, UTM_CAMPAIGNS, UTM_CONTENTS, UTM_MEDIUMS_ALL, UTM_MEDIUMS_APP, UTM_MEDIUMS_MESSAGING, UTM_MEDIUMS_WEB, UTM_SOURCES, UTM_SOURCES_REQUIRING_SELLER, UTM_TERMS, UtmProvider, aggregateRatingFromTestimonials, buildBlockedError, buildPageGraph, buildUtmUrl, canonicalUrl, classifyAndEnforce, classifyUrl, createAnalyticsLoader, createAuditEntry, createEntityIds, createSchemaGenerators, isUtmExempt, isValidUtmParams, loadClarity, loadGoogleAnalytics, parseUtmParams, requiresSellerAttribution, requiresUtm, toFirstTouchAttribution, useAnalytics, useCookieConsent, useUtmDefaults, validateCompliance, validateUtmField };
1299
+ //# sourceMappingURL=out.js.map
907
1300
  //# sourceMappingURL=index.js.map