@brandlab-digital/site-core 3.2.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -35,13 +35,20 @@ export interface BrandlabAnalyticsContract {
35
35
  websiteCalls?: {
36
36
  conversionLabelEnv: string;
37
37
  };
38
- /** Enhanced Conversions require both this project opt-in and live user consent. */
38
+ /**
39
+ * Enhanced Conversions send the enquirer's email and phone (hashed by the
40
+ * Google tag) with the Ads form conversion.
41
+ * - `consent-required`: only when the site passes a live ad_user_data grant.
42
+ * - `disclosed`: the site's privacy policy discloses this measurement use, so
43
+ * ad_user_data defaults to granted. ad_personalization stays denied.
44
+ */
39
45
  enhancedConversions?: {
40
- mode: 'consent-required';
46
+ mode: BrandlabEnhancedConversionsMode;
41
47
  };
42
48
  };
43
49
  additionalEvents?: readonly BrandlabAnalyticsEventContract[];
44
50
  }
51
+ export type BrandlabEnhancedConversionsMode = 'consent-required' | 'disclosed';
45
52
  export type GtagTransport = (...args: ['event', string, Record<string, unknown>] | ['set', string, Record<string, unknown>] | ['config', string, Record<string, unknown>?] | ['js', Date]) => void;
46
53
  export interface EnhancedConversionUserData {
47
54
  email?: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../src/analytics.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../src/analytics.ts"],"names":[],"mappings":"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"}
package/dist/analytics.js CHANGED
@@ -75,8 +75,9 @@ export function defineAnalyticsContract(contract) {
75
75
  assertAnalyticsEnvKey(contract.googleAds.websiteCalls.conversionLabelEnv, 'googleAds.websiteCalls.conversionLabelEnv');
76
76
  }
77
77
  if (contract.googleAds.enhancedConversions !== undefined &&
78
- contract.googleAds.enhancedConversions.mode !== 'consent-required') {
79
- throw new Error('googleAds.enhancedConversions must require consent.');
78
+ contract.googleAds.enhancedConversions.mode !== 'consent-required' &&
79
+ contract.googleAds.enhancedConversions.mode !== 'disclosed') {
80
+ throw new Error("googleAds.enhancedConversions.mode must be 'consent-required' or 'disclosed'.");
80
81
  }
81
82
  }
82
83
  const events = contract.additionalEvents ?? [];
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.js","sourceRoot":"","sources":["../src/analytics.ts"],"names":[],"mappings":"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","sourcesContent":["import { normalizeBrandlabRoutePath } from './routes.js'\nimport { assertBrandlabCanonicalEntityId } from './entity-id.js'\n\nexport const CANONICAL_LEAD_EVENTS = ['contact_form'] as const\nexport const CANONICAL_PHONE_INTERACTION_EVENTS = ['phone_link_click'] as const\nexport const BRANDLAB_LEAD_DIAGNOSTIC_EVENTS = [\n 'lead_form_start',\n 'lead_form_error',\n 'lead_cta_click',\n] as const\n\nexport type CanonicalLeadEvent = (typeof CANONICAL_LEAD_EVENTS)[number]\nexport type CanonicalPhoneInteractionEvent =\n (typeof CANONICAL_PHONE_INTERACTION_EVENTS)[number]\nexport type BrandlabLeadDiagnosticEvent =\n (typeof BRANDLAB_LEAD_DIAGNOSTIC_EVENTS)[number]\n\nexport type BrandlabAnalyticsParameterPolicy =\n | { key: string; type: 'boolean' }\n | { key: string; type: 'count'; minimum?: number; maximum?: number }\n | { key: string; type: 'enum'; values: readonly string[] }\n | { key: string; type: 'slug' }\n\nexport interface BrandlabAnalyticsEventContract {\n name: string\n parameters: readonly BrandlabAnalyticsParameterPolicy[]\n}\n\nexport interface BrandlabAnalyticsContract {\n /** Canonical entity binding prevents analytics config being reused by the wrong site. */\n projectId: string\n measurementIdEnv: string\n googleAds?: {\n conversionIdEnv: string\n contactConversionLabelEnv: string\n /** Google Ads forwarding-number label for genuine calls from the website. */\n websiteCalls?: {\n conversionLabelEnv: string\n }\n /** Enhanced Conversions require both this project opt-in and live user consent. */\n enhancedConversions?: { mode: 'consent-required' }\n }\n additionalEvents?: readonly BrandlabAnalyticsEventContract[]\n}\nexport type GtagTransport = (...args:\n | ['event', string, Record<string, unknown>]\n | ['set', string, Record<string, unknown>]\n | ['config', string, Record<string, unknown>?]\n | ['js', Date]\n) => void\n\nexport interface EnhancedConversionUserData {\n email?: string | null\n phone?: string | null\n}\n\nexport interface GoogleTagEnhancedConversionUserData extends Record<string, unknown> {\n email: string\n phone_number?: string\n}\n\nexport interface ContactFormConversionResult {\n ga4Sent: boolean\n enhancedConversionSent: boolean\n adsConversionSent: boolean\n}\n\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\nconst GMAIL_DOMAINS = new Set(['gmail.com', 'googlemail.com'])\nconst AUSTRALIAN_E164_DIGITS = /^61[23478]\\d{8}$/\nconst GA4_CONTACT_FORM_PARAM_KEYS = new Set([\n 'event_category',\n 'location_slug',\n 'page_path',\n 'page_template',\n 'service_slug',\n 'submission_id',\n])\nconst GOOGLE_ADS_CONVERSION_PARAM_KEYS = new Set([\n 'currency',\n 'transaction_id',\n 'value',\n])\nconst ANALYTICS_ENV_KEY_PATTERN = /^[A-Z][A-Z0-9_]*$/\nconst ANALYTICS_EVENT_PATTERN = /^[a-z][a-z0-9_]{0,39}$/\nconst PROHIBITED_ANALYTICS_PARAM_TOKENS = [\n 'address',\n 'email',\n 'first_name',\n 'last_name',\n 'message',\n 'name',\n 'phone',\n] as const\nconst PRIVACY_SAFE_SUBMISSION_ID_PATTERN = /^[a-f0-9]{64}$/\n\nfunction looksLikePersonalIdentifier(value: string) {\n const normalized = value.trim().toLowerCase()\n const digits = normalized.replace(/\\D/g, '')\n return (\n normalized.includes('@') ||\n digits.length >= 8 ||\n /^\\d{4}$/.test(normalized) ||\n /^[a-f0-9]{24,}$/.test(normalized) ||\n /\\b\\d+\\s+[a-z][a-z -]+\\s+(?:street|st|road|rd|avenue|ave|lane|ln|drive|dr|place|pl|court|ct)\\b/.test(\n normalized,\n )\n )\n}\n\nconst CONTRACT_KEYS = ['projectId', 'measurementIdEnv', 'googleAds', 'additionalEvents']\nconst GOOGLE_ADS_KEYS = ['conversionIdEnv', 'contactConversionLabelEnv', 'websiteCalls', 'enhancedConversions']\n\nfunction assertContractKeys(value: object, allowed: readonly string[], field: string) {\n const unsupported = Object.keys(value).filter((key) => !allowed.includes(key))\n if (unsupported.length > 0) {\n throw new Error(`${field} contains unsupported keys: ${unsupported.join(', ')}.`)\n }\n}\n\nfunction assertAnalyticsEnvKey(value: string, field: string) {\n if (!ANALYTICS_ENV_KEY_PATTERN.test(value)) {\n throw new Error(`${field} must be an environment variable key.`)\n }\n}\n\nexport function defineAnalyticsContract<const T extends BrandlabAnalyticsContract>(contract: T): T {\n if (!contract || typeof contract !== 'object' || Array.isArray(contract)) {\n throw new Error('Analytics contract must be an object.')\n }\n // Retired 3.0 flags fail here instead of being silently ignored: web vitals\n // were removed (page speed comes from Search Console) and the privacy-safe\n // lead diagnostics are always on.\n assertContractKeys(contract, CONTRACT_KEYS, 'Analytics contract')\n assertBrandlabCanonicalEntityId(contract.projectId)\n assertAnalyticsEnvKey(contract.measurementIdEnv, 'measurementIdEnv')\n if (contract.googleAds) {\n assertContractKeys(contract.googleAds, GOOGLE_ADS_KEYS, 'googleAds')\n assertAnalyticsEnvKey(contract.googleAds.conversionIdEnv, 'googleAds.conversionIdEnv')\n assertAnalyticsEnvKey(\n contract.googleAds.contactConversionLabelEnv,\n 'googleAds.contactConversionLabelEnv',\n )\n if (contract.googleAds.websiteCalls) {\n assertAnalyticsEnvKey(\n contract.googleAds.websiteCalls.conversionLabelEnv,\n 'googleAds.websiteCalls.conversionLabelEnv',\n )\n }\n if (\n contract.googleAds.enhancedConversions !== undefined &&\n contract.googleAds.enhancedConversions.mode !== 'consent-required'\n ) {\n throw new Error('googleAds.enhancedConversions must require consent.')\n }\n }\n\n const events = contract.additionalEvents ?? []\n const duplicate = events.find((event, index) =>\n events.slice(0, index).some((candidate) => candidate.name === event.name),\n )\n if (duplicate) throw new Error(`Analytics contract contains duplicate event \"${duplicate.name}\".`)\n\n const reservedDiagnostic = events.find((event) =>\n BRANDLAB_LEAD_DIAGNOSTIC_EVENTS.includes(event.name as BrandlabLeadDiagnosticEvent) ||\n CANONICAL_PHONE_INTERACTION_EVENTS.includes(\n event.name as CanonicalPhoneInteractionEvent,\n ),\n )\n if (reservedDiagnostic) {\n throw new Error(\n `Shared lead diagnostic event \"${reservedDiagnostic.name}\" must not be redefined.`,\n )\n }\n\n events.forEach((event, eventIndex) => {\n if (!ANALYTICS_EVENT_PATTERN.test(event.name)) {\n throw new Error(`additionalEvents[${eventIndex}].name is invalid.`)\n }\n if (isCanonicalLeadEvent(event.name)) {\n throw new Error(`Canonical lead event \"${event.name}\" must not be redefined.`)\n }\n if (event.parameters.length === 0 || event.parameters.length > 20) {\n throw new Error(`additionalEvents[${eventIndex}].parameters must contain 1 to 20 policies.`)\n }\n const duplicateParameter = event.parameters.find((parameter, index) =>\n event.parameters.slice(0, index).some((candidate) => candidate.key === parameter.key),\n )\n if (duplicateParameter) {\n throw new Error(`Analytics event \"${event.name}\" contains duplicate parameter \"${duplicateParameter.key}\".`)\n }\n event.parameters.forEach((parameter, keyIndex) => {\n const key = parameter.key\n if (!ANALYTICS_EVENT_PATTERN.test(key)) {\n throw new Error(`additionalEvents[${eventIndex}].parameters[${keyIndex}].key is invalid.`)\n }\n const normalized = key.toLowerCase()\n if (PROHIBITED_ANALYTICS_PARAM_TOKENS.some((token) => normalized.includes(token))) {\n throw new Error(`Analytics parameter \"${key}\" may contain personal information.`)\n }\n if (parameter.type === 'enum') {\n if (\n parameter.values.length === 0 ||\n parameter.values.length > 50 ||\n new Set(parameter.values).size !== parameter.values.length ||\n parameter.values.some(\n (value) =>\n !value.trim() ||\n value.length > 80 ||\n looksLikePersonalIdentifier(value),\n )\n ) {\n throw new Error(`Analytics enum parameter \"${key}\" has an unsafe value policy.`)\n }\n } else if (parameter.type === 'count') {\n const minimum = parameter.minimum ?? 0\n const maximum = parameter.maximum ?? 100_000\n if (\n !Number.isInteger(minimum) ||\n !Number.isInteger(maximum) ||\n minimum < 0 ||\n maximum < minimum ||\n maximum > 1_000_000\n ) {\n throw new Error(`Analytics count parameter \"${key}\" has an invalid range.`)\n }\n }\n })\n })\n\n return contract\n}\n\nexport function isCanonicalLeadEvent(value: string): value is CanonicalLeadEvent {\n return CANONICAL_LEAD_EVENTS.includes(value as CanonicalLeadEvent)\n}\n\nexport function isCanonicalPhoneInteractionEvent(\n value: string,\n): value is CanonicalPhoneInteractionEvent {\n return CANONICAL_PHONE_INTERACTION_EVENTS.includes(\n value as CanonicalPhoneInteractionEvent,\n )\n}\n\nexport function normalizeGoogleAdsConversionId(conversionId: string) {\n const trimmed = conversionId.trim()\n return trimmed.startsWith('AW-') ? trimmed : `AW-${trimmed}`\n}\n\nfunction isGoogleAdsConversionLabel(value: string) {\n return /^[A-Za-z0-9_-]{1,100}$/.test(value)\n}\n\nexport function normalizeEnhancedConversionEmail(email: string | null | undefined) {\n const trimmed = email?.trim().toLowerCase()\n if (!trimmed || !EMAIL_PATTERN.test(trimmed)) {\n return undefined\n }\n\n const [localPart, domain] = trimmed.split('@')\n if (!localPart || !domain) {\n return undefined\n }\n\n const normalizedLocalPart = GMAIL_DOMAINS.has(domain)\n ? localPart.replace(/\\./g, '')\n : localPart\n\n return `${normalizedLocalPart}@${domain}`\n}\n\nexport function normalizeEnhancedConversionPhone(phone: string | null | undefined) {\n const digits = phone?.replace(/[^\\d]/g, '') ?? ''\n if (!digits) {\n return undefined\n }\n\n const normalizedDigits = digits.startsWith('0') ? `61${digits.slice(1)}` : digits\n\n return AUSTRALIAN_E164_DIGITS.test(normalizedDigits) ? `+${normalizedDigits}` : undefined\n}\n\nexport function buildEnhancedConversionUserData(\n userData: EnhancedConversionUserData | null | undefined,\n): GoogleTagEnhancedConversionUserData | undefined {\n const email = normalizeEnhancedConversionEmail(userData?.email)\n if (!email) {\n return undefined\n }\n\n const phoneNumber = normalizeEnhancedConversionPhone(userData?.phone)\n\n return phoneNumber\n ? {\n email,\n phone_number: phoneNumber,\n }\n : { email }\n}\n\nexport function getGoogleAdsContactSendTo(options: {\n conversionId?: string | null\n conversionLabel?: string | null\n}) {\n const conversionId = options.conversionId?.trim()\n const conversionLabel = options.conversionLabel?.trim()\n\n if (!conversionId || !conversionLabel || !isGoogleAdsConversionLabel(conversionLabel)) {\n return undefined\n }\n\n return `${normalizeGoogleAdsConversionId(conversionId)}/${conversionLabel}`\n}\n\nexport function getGoogleAdsWebsiteCallSendTo(options: {\n conversionId?: string | null\n conversionLabel?: string | null\n}) {\n return getGoogleAdsContactSendTo(options)\n}\n\nexport function sendBrandlabAnalyticsEvent(options: {\n enabled: boolean\n measurementId?: string | null\n gtag?: GtagTransport\n eventName: string\n params: Record<string, unknown>\n}) {\n if (!options.enabled || !options.measurementId || typeof options.gtag !== 'function') {\n return false\n }\n\n options.gtag('event', options.eventName, {\n ...options.params,\n send_to: options.measurementId,\n })\n\n return true\n}\n\nexport function sanitizeContactFormGa4Params(params: Record<string, unknown>) {\n return Object.entries(params).reduce<Record<string, unknown>>((sanitized, [key, value]) => {\n const normalizedKey = key.toLowerCase()\n if (!GA4_CONTACT_FORM_PARAM_KEYS.has(normalizedKey)) return sanitized\n if (normalizedKey === 'event_category') {\n if (value === 'lead') sanitized.event_category = 'lead'\n return sanitized\n }\n if (normalizedKey === 'page_path') {\n if (typeof value !== 'string') return sanitized\n const path = value.trim().split(/[?#]/, 1)[0]\n if (!path || path.length > 500) return sanitized\n try {\n sanitized.page_path = normalizeBrandlabRoutePath(path)\n } catch {\n // Invalid or encoded path separators are not analytics dimensions.\n }\n return sanitized\n }\n if (normalizedKey === 'submission_id') {\n if (typeof value === 'string' && PRIVACY_SAFE_SUBMISSION_ID_PATTERN.test(value)) {\n sanitized.submission_id = value\n }\n return sanitized\n }\n if (\n typeof value === 'string' &&\n /^[a-z0-9]+(?:[-_][a-z0-9]+)*$/.test(value) &&\n value.length <= 80\n ) {\n sanitized[normalizedKey] = value\n }\n\n return sanitized\n }, {})\n}\n\nfunction sanitizeGoogleAdsConversionParams(params: Record<string, unknown>) {\n return Object.entries(params).reduce<Record<string, unknown>>((sanitized, [key, value]) => {\n const normalizedKey = key.toLowerCase()\n if (!GOOGLE_ADS_CONVERSION_PARAM_KEYS.has(normalizedKey)) return sanitized\n if (normalizedKey === 'currency' && typeof value === 'string' && /^[A-Z]{3}$/.test(value)) {\n sanitized.currency = value\n } else if (\n normalizedKey === 'value' &&\n typeof value === 'number' &&\n Number.isFinite(value) &&\n value >= 0\n ) {\n sanitized.value = value\n } else if (\n normalizedKey === 'transaction_id' &&\n typeof value === 'string' &&\n PRIVACY_SAFE_SUBMISSION_ID_PATTERN.test(value)\n ) {\n sanitized.transaction_id = value\n }\n\n return sanitized\n }, {})\n}\n\nexport function sendContactFormConversion(options: {\n enabled: boolean\n measurementId?: string | null\n googleAdsConversionId?: string | null\n googleAdsConversionLabel?: string | null\n gtag?: GtagTransport\n pagePath: string\n submissionId?: string | null\n enhancedConversion?: EnhancedConversionUserData | null\n enhancedConversionsEnabled?: boolean\n adUserDataConsent?: boolean\n params?: Record<string, unknown>\n adsParams?: Record<string, unknown>\n}): ContactFormConversionResult {\n const submissionId = options.submissionId?.trim().toLowerCase()\n const privacySafeSubmissionId =\n submissionId && PRIVACY_SAFE_SUBMISSION_ID_PATTERN.test(submissionId)\n ? submissionId\n : undefined\n const baseParams = sanitizeContactFormGa4Params({\n ...options.params,\n page_path: options.pagePath,\n ...(privacySafeSubmissionId ? { submission_id: privacySafeSubmissionId } : {}),\n event_category: 'lead',\n })\n const ga4Sent = sendBrandlabAnalyticsEvent({\n enabled: options.enabled,\n measurementId: options.measurementId,\n gtag: options.gtag,\n eventName: 'contact_form',\n params: baseParams,\n })\n const sendTo = getGoogleAdsContactSendTo({\n conversionId: options.googleAdsConversionId,\n conversionLabel: options.googleAdsConversionLabel,\n })\n const userData =\n options.enhancedConversionsEnabled && options.adUserDataConsent\n ? buildEnhancedConversionUserData(options.enhancedConversion)\n : undefined\n const canSendAdsConversion =\n options.enabled &&\n typeof options.gtag === 'function' &&\n Boolean(sendTo)\n\n if (!canSendAdsConversion || !sendTo || typeof options.gtag !== 'function') {\n return {\n ga4Sent,\n enhancedConversionSent: false,\n adsConversionSent: false,\n }\n }\n\n if (userData) {\n options.gtag('set', 'user_data', userData)\n }\n try {\n options.gtag('event', 'conversion', {\n ...sanitizeGoogleAdsConversionParams(options.adsParams ?? {}),\n send_to: sendTo,\n ...(privacySafeSubmissionId ? { transaction_id: privacySafeSubmissionId } : {}),\n })\n } finally {\n if (userData) options.gtag('set', 'user_data', {})\n }\n\n return {\n ga4Sent,\n enhancedConversionSent: Boolean(userData),\n adsConversionSent: true,\n }\n}\n"]}
1
+ {"version":3,"file":"analytics.js","sourceRoot":"","sources":["../src/analytics.ts"],"names":[],"mappings":"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","sourcesContent":["import { normalizeBrandlabRoutePath } from './routes.js'\nimport { assertBrandlabCanonicalEntityId } from './entity-id.js'\n\nexport const CANONICAL_LEAD_EVENTS = ['contact_form'] as const\nexport const CANONICAL_PHONE_INTERACTION_EVENTS = ['phone_link_click'] as const\nexport const BRANDLAB_LEAD_DIAGNOSTIC_EVENTS = [\n 'lead_form_start',\n 'lead_form_error',\n 'lead_cta_click',\n] as const\n\nexport type CanonicalLeadEvent = (typeof CANONICAL_LEAD_EVENTS)[number]\nexport type CanonicalPhoneInteractionEvent =\n (typeof CANONICAL_PHONE_INTERACTION_EVENTS)[number]\nexport type BrandlabLeadDiagnosticEvent =\n (typeof BRANDLAB_LEAD_DIAGNOSTIC_EVENTS)[number]\n\nexport type BrandlabAnalyticsParameterPolicy =\n | { key: string; type: 'boolean' }\n | { key: string; type: 'count'; minimum?: number; maximum?: number }\n | { key: string; type: 'enum'; values: readonly string[] }\n | { key: string; type: 'slug' }\n\nexport interface BrandlabAnalyticsEventContract {\n name: string\n parameters: readonly BrandlabAnalyticsParameterPolicy[]\n}\n\nexport interface BrandlabAnalyticsContract {\n /** Canonical entity binding prevents analytics config being reused by the wrong site. */\n projectId: string\n measurementIdEnv: string\n googleAds?: {\n conversionIdEnv: string\n contactConversionLabelEnv: string\n /** Google Ads forwarding-number label for genuine calls from the website. */\n websiteCalls?: {\n conversionLabelEnv: string\n }\n /**\n * Enhanced Conversions send the enquirer's email and phone (hashed by the\n * Google tag) with the Ads form conversion.\n * - `consent-required`: only when the site passes a live ad_user_data grant.\n * - `disclosed`: the site's privacy policy discloses this measurement use, so\n * ad_user_data defaults to granted. ad_personalization stays denied.\n */\n enhancedConversions?: { mode: BrandlabEnhancedConversionsMode }\n }\n additionalEvents?: readonly BrandlabAnalyticsEventContract[]\n}\nexport type BrandlabEnhancedConversionsMode = 'consent-required' | 'disclosed'\n\nexport type GtagTransport = (...args:\n | ['event', string, Record<string, unknown>]\n | ['set', string, Record<string, unknown>]\n | ['config', string, Record<string, unknown>?]\n | ['js', Date]\n) => void\n\nexport interface EnhancedConversionUserData {\n email?: string | null\n phone?: string | null\n}\n\nexport interface GoogleTagEnhancedConversionUserData extends Record<string, unknown> {\n email: string\n phone_number?: string\n}\n\nexport interface ContactFormConversionResult {\n ga4Sent: boolean\n enhancedConversionSent: boolean\n adsConversionSent: boolean\n}\n\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\nconst GMAIL_DOMAINS = new Set(['gmail.com', 'googlemail.com'])\nconst AUSTRALIAN_E164_DIGITS = /^61[23478]\\d{8}$/\nconst GA4_CONTACT_FORM_PARAM_KEYS = new Set([\n 'event_category',\n 'location_slug',\n 'page_path',\n 'page_template',\n 'service_slug',\n 'submission_id',\n])\nconst GOOGLE_ADS_CONVERSION_PARAM_KEYS = new Set([\n 'currency',\n 'transaction_id',\n 'value',\n])\nconst ANALYTICS_ENV_KEY_PATTERN = /^[A-Z][A-Z0-9_]*$/\nconst ANALYTICS_EVENT_PATTERN = /^[a-z][a-z0-9_]{0,39}$/\nconst PROHIBITED_ANALYTICS_PARAM_TOKENS = [\n 'address',\n 'email',\n 'first_name',\n 'last_name',\n 'message',\n 'name',\n 'phone',\n] as const\nconst PRIVACY_SAFE_SUBMISSION_ID_PATTERN = /^[a-f0-9]{64}$/\n\nfunction looksLikePersonalIdentifier(value: string) {\n const normalized = value.trim().toLowerCase()\n const digits = normalized.replace(/\\D/g, '')\n return (\n normalized.includes('@') ||\n digits.length >= 8 ||\n /^\\d{4}$/.test(normalized) ||\n /^[a-f0-9]{24,}$/.test(normalized) ||\n /\\b\\d+\\s+[a-z][a-z -]+\\s+(?:street|st|road|rd|avenue|ave|lane|ln|drive|dr|place|pl|court|ct)\\b/.test(\n normalized,\n )\n )\n}\n\nconst CONTRACT_KEYS = ['projectId', 'measurementIdEnv', 'googleAds', 'additionalEvents']\nconst GOOGLE_ADS_KEYS = ['conversionIdEnv', 'contactConversionLabelEnv', 'websiteCalls', 'enhancedConversions']\n\nfunction assertContractKeys(value: object, allowed: readonly string[], field: string) {\n const unsupported = Object.keys(value).filter((key) => !allowed.includes(key))\n if (unsupported.length > 0) {\n throw new Error(`${field} contains unsupported keys: ${unsupported.join(', ')}.`)\n }\n}\n\nfunction assertAnalyticsEnvKey(value: string, field: string) {\n if (!ANALYTICS_ENV_KEY_PATTERN.test(value)) {\n throw new Error(`${field} must be an environment variable key.`)\n }\n}\n\nexport function defineAnalyticsContract<const T extends BrandlabAnalyticsContract>(contract: T): T {\n if (!contract || typeof contract !== 'object' || Array.isArray(contract)) {\n throw new Error('Analytics contract must be an object.')\n }\n // Retired 3.0 flags fail here instead of being silently ignored: web vitals\n // were removed (page speed comes from Search Console) and the privacy-safe\n // lead diagnostics are always on.\n assertContractKeys(contract, CONTRACT_KEYS, 'Analytics contract')\n assertBrandlabCanonicalEntityId(contract.projectId)\n assertAnalyticsEnvKey(contract.measurementIdEnv, 'measurementIdEnv')\n if (contract.googleAds) {\n assertContractKeys(contract.googleAds, GOOGLE_ADS_KEYS, 'googleAds')\n assertAnalyticsEnvKey(contract.googleAds.conversionIdEnv, 'googleAds.conversionIdEnv')\n assertAnalyticsEnvKey(\n contract.googleAds.contactConversionLabelEnv,\n 'googleAds.contactConversionLabelEnv',\n )\n if (contract.googleAds.websiteCalls) {\n assertAnalyticsEnvKey(\n contract.googleAds.websiteCalls.conversionLabelEnv,\n 'googleAds.websiteCalls.conversionLabelEnv',\n )\n }\n if (\n contract.googleAds.enhancedConversions !== undefined &&\n contract.googleAds.enhancedConversions.mode !== 'consent-required' &&\n contract.googleAds.enhancedConversions.mode !== 'disclosed'\n ) {\n throw new Error(\"googleAds.enhancedConversions.mode must be 'consent-required' or 'disclosed'.\")\n }\n }\n\n const events = contract.additionalEvents ?? []\n const duplicate = events.find((event, index) =>\n events.slice(0, index).some((candidate) => candidate.name === event.name),\n )\n if (duplicate) throw new Error(`Analytics contract contains duplicate event \"${duplicate.name}\".`)\n\n const reservedDiagnostic = events.find((event) =>\n BRANDLAB_LEAD_DIAGNOSTIC_EVENTS.includes(event.name as BrandlabLeadDiagnosticEvent) ||\n CANONICAL_PHONE_INTERACTION_EVENTS.includes(\n event.name as CanonicalPhoneInteractionEvent,\n ),\n )\n if (reservedDiagnostic) {\n throw new Error(\n `Shared lead diagnostic event \"${reservedDiagnostic.name}\" must not be redefined.`,\n )\n }\n\n events.forEach((event, eventIndex) => {\n if (!ANALYTICS_EVENT_PATTERN.test(event.name)) {\n throw new Error(`additionalEvents[${eventIndex}].name is invalid.`)\n }\n if (isCanonicalLeadEvent(event.name)) {\n throw new Error(`Canonical lead event \"${event.name}\" must not be redefined.`)\n }\n if (event.parameters.length === 0 || event.parameters.length > 20) {\n throw new Error(`additionalEvents[${eventIndex}].parameters must contain 1 to 20 policies.`)\n }\n const duplicateParameter = event.parameters.find((parameter, index) =>\n event.parameters.slice(0, index).some((candidate) => candidate.key === parameter.key),\n )\n if (duplicateParameter) {\n throw new Error(`Analytics event \"${event.name}\" contains duplicate parameter \"${duplicateParameter.key}\".`)\n }\n event.parameters.forEach((parameter, keyIndex) => {\n const key = parameter.key\n if (!ANALYTICS_EVENT_PATTERN.test(key)) {\n throw new Error(`additionalEvents[${eventIndex}].parameters[${keyIndex}].key is invalid.`)\n }\n const normalized = key.toLowerCase()\n if (PROHIBITED_ANALYTICS_PARAM_TOKENS.some((token) => normalized.includes(token))) {\n throw new Error(`Analytics parameter \"${key}\" may contain personal information.`)\n }\n if (parameter.type === 'enum') {\n if (\n parameter.values.length === 0 ||\n parameter.values.length > 50 ||\n new Set(parameter.values).size !== parameter.values.length ||\n parameter.values.some(\n (value) =>\n !value.trim() ||\n value.length > 80 ||\n looksLikePersonalIdentifier(value),\n )\n ) {\n throw new Error(`Analytics enum parameter \"${key}\" has an unsafe value policy.`)\n }\n } else if (parameter.type === 'count') {\n const minimum = parameter.minimum ?? 0\n const maximum = parameter.maximum ?? 100_000\n if (\n !Number.isInteger(minimum) ||\n !Number.isInteger(maximum) ||\n minimum < 0 ||\n maximum < minimum ||\n maximum > 1_000_000\n ) {\n throw new Error(`Analytics count parameter \"${key}\" has an invalid range.`)\n }\n }\n })\n })\n\n return contract\n}\n\nexport function isCanonicalLeadEvent(value: string): value is CanonicalLeadEvent {\n return CANONICAL_LEAD_EVENTS.includes(value as CanonicalLeadEvent)\n}\n\nexport function isCanonicalPhoneInteractionEvent(\n value: string,\n): value is CanonicalPhoneInteractionEvent {\n return CANONICAL_PHONE_INTERACTION_EVENTS.includes(\n value as CanonicalPhoneInteractionEvent,\n )\n}\n\nexport function normalizeGoogleAdsConversionId(conversionId: string) {\n const trimmed = conversionId.trim()\n return trimmed.startsWith('AW-') ? trimmed : `AW-${trimmed}`\n}\n\nfunction isGoogleAdsConversionLabel(value: string) {\n return /^[A-Za-z0-9_-]{1,100}$/.test(value)\n}\n\nexport function normalizeEnhancedConversionEmail(email: string | null | undefined) {\n const trimmed = email?.trim().toLowerCase()\n if (!trimmed || !EMAIL_PATTERN.test(trimmed)) {\n return undefined\n }\n\n const [localPart, domain] = trimmed.split('@')\n if (!localPart || !domain) {\n return undefined\n }\n\n const normalizedLocalPart = GMAIL_DOMAINS.has(domain)\n ? localPart.replace(/\\./g, '')\n : localPart\n\n return `${normalizedLocalPart}@${domain}`\n}\n\nexport function normalizeEnhancedConversionPhone(phone: string | null | undefined) {\n const digits = phone?.replace(/[^\\d]/g, '') ?? ''\n if (!digits) {\n return undefined\n }\n\n const normalizedDigits = digits.startsWith('0') ? `61${digits.slice(1)}` : digits\n\n return AUSTRALIAN_E164_DIGITS.test(normalizedDigits) ? `+${normalizedDigits}` : undefined\n}\n\nexport function buildEnhancedConversionUserData(\n userData: EnhancedConversionUserData | null | undefined,\n): GoogleTagEnhancedConversionUserData | undefined {\n const email = normalizeEnhancedConversionEmail(userData?.email)\n if (!email) {\n return undefined\n }\n\n const phoneNumber = normalizeEnhancedConversionPhone(userData?.phone)\n\n return phoneNumber\n ? {\n email,\n phone_number: phoneNumber,\n }\n : { email }\n}\n\nexport function getGoogleAdsContactSendTo(options: {\n conversionId?: string | null\n conversionLabel?: string | null\n}) {\n const conversionId = options.conversionId?.trim()\n const conversionLabel = options.conversionLabel?.trim()\n\n if (!conversionId || !conversionLabel || !isGoogleAdsConversionLabel(conversionLabel)) {\n return undefined\n }\n\n return `${normalizeGoogleAdsConversionId(conversionId)}/${conversionLabel}`\n}\n\nexport function getGoogleAdsWebsiteCallSendTo(options: {\n conversionId?: string | null\n conversionLabel?: string | null\n}) {\n return getGoogleAdsContactSendTo(options)\n}\n\nexport function sendBrandlabAnalyticsEvent(options: {\n enabled: boolean\n measurementId?: string | null\n gtag?: GtagTransport\n eventName: string\n params: Record<string, unknown>\n}) {\n if (!options.enabled || !options.measurementId || typeof options.gtag !== 'function') {\n return false\n }\n\n options.gtag('event', options.eventName, {\n ...options.params,\n send_to: options.measurementId,\n })\n\n return true\n}\n\nexport function sanitizeContactFormGa4Params(params: Record<string, unknown>) {\n return Object.entries(params).reduce<Record<string, unknown>>((sanitized, [key, value]) => {\n const normalizedKey = key.toLowerCase()\n if (!GA4_CONTACT_FORM_PARAM_KEYS.has(normalizedKey)) return sanitized\n if (normalizedKey === 'event_category') {\n if (value === 'lead') sanitized.event_category = 'lead'\n return sanitized\n }\n if (normalizedKey === 'page_path') {\n if (typeof value !== 'string') return sanitized\n const path = value.trim().split(/[?#]/, 1)[0]\n if (!path || path.length > 500) return sanitized\n try {\n sanitized.page_path = normalizeBrandlabRoutePath(path)\n } catch {\n // Invalid or encoded path separators are not analytics dimensions.\n }\n return sanitized\n }\n if (normalizedKey === 'submission_id') {\n if (typeof value === 'string' && PRIVACY_SAFE_SUBMISSION_ID_PATTERN.test(value)) {\n sanitized.submission_id = value\n }\n return sanitized\n }\n if (\n typeof value === 'string' &&\n /^[a-z0-9]+(?:[-_][a-z0-9]+)*$/.test(value) &&\n value.length <= 80\n ) {\n sanitized[normalizedKey] = value\n }\n\n return sanitized\n }, {})\n}\n\nfunction sanitizeGoogleAdsConversionParams(params: Record<string, unknown>) {\n return Object.entries(params).reduce<Record<string, unknown>>((sanitized, [key, value]) => {\n const normalizedKey = key.toLowerCase()\n if (!GOOGLE_ADS_CONVERSION_PARAM_KEYS.has(normalizedKey)) return sanitized\n if (normalizedKey === 'currency' && typeof value === 'string' && /^[A-Z]{3}$/.test(value)) {\n sanitized.currency = value\n } else if (\n normalizedKey === 'value' &&\n typeof value === 'number' &&\n Number.isFinite(value) &&\n value >= 0\n ) {\n sanitized.value = value\n } else if (\n normalizedKey === 'transaction_id' &&\n typeof value === 'string' &&\n PRIVACY_SAFE_SUBMISSION_ID_PATTERN.test(value)\n ) {\n sanitized.transaction_id = value\n }\n\n return sanitized\n }, {})\n}\n\nexport function sendContactFormConversion(options: {\n enabled: boolean\n measurementId?: string | null\n googleAdsConversionId?: string | null\n googleAdsConversionLabel?: string | null\n gtag?: GtagTransport\n pagePath: string\n submissionId?: string | null\n enhancedConversion?: EnhancedConversionUserData | null\n enhancedConversionsEnabled?: boolean\n adUserDataConsent?: boolean\n params?: Record<string, unknown>\n adsParams?: Record<string, unknown>\n}): ContactFormConversionResult {\n const submissionId = options.submissionId?.trim().toLowerCase()\n const privacySafeSubmissionId =\n submissionId && PRIVACY_SAFE_SUBMISSION_ID_PATTERN.test(submissionId)\n ? submissionId\n : undefined\n const baseParams = sanitizeContactFormGa4Params({\n ...options.params,\n page_path: options.pagePath,\n ...(privacySafeSubmissionId ? { submission_id: privacySafeSubmissionId } : {}),\n event_category: 'lead',\n })\n const ga4Sent = sendBrandlabAnalyticsEvent({\n enabled: options.enabled,\n measurementId: options.measurementId,\n gtag: options.gtag,\n eventName: 'contact_form',\n params: baseParams,\n })\n const sendTo = getGoogleAdsContactSendTo({\n conversionId: options.googleAdsConversionId,\n conversionLabel: options.googleAdsConversionLabel,\n })\n const userData =\n options.enhancedConversionsEnabled && options.adUserDataConsent\n ? buildEnhancedConversionUserData(options.enhancedConversion)\n : undefined\n const canSendAdsConversion =\n options.enabled &&\n typeof options.gtag === 'function' &&\n Boolean(sendTo)\n\n if (!canSendAdsConversion || !sendTo || typeof options.gtag !== 'function') {\n return {\n ga4Sent,\n enhancedConversionSent: false,\n adsConversionSent: false,\n }\n }\n\n if (userData) {\n options.gtag('set', 'user_data', userData)\n }\n try {\n options.gtag('event', 'conversion', {\n ...sanitizeGoogleAdsConversionParams(options.adsParams ?? {}),\n send_to: sendTo,\n ...(privacySafeSubmissionId ? { transaction_id: privacySafeSubmissionId } : {}),\n })\n } finally {\n if (userData) options.gtag('set', 'user_data', {})\n }\n\n return {\n ga4Sent,\n enhancedConversionSent: Boolean(userData),\n adsConversionSent: true,\n }\n}\n"]}
@@ -122,5 +122,45 @@ export declare function unknownFact<T>(): BrandlabFact<T>;
122
122
  export declare function notApplicableFact<T>(reason: string): BrandlabFact<T>;
123
123
  export declare function resolveVerifiedFactForSurface<T>(fact: BrandlabFact<T>, surface: BrandlabEvidenceSurface, asOf?: Date): T | undefined;
124
124
  export declare function defineBrandlabBusiness<const T extends BrandlabBusiness>(business: T): T;
125
+ /**
126
+ * The business facts as a site displays them: the verified values without their
127
+ * evidence (unknown and not-applicable facts are absent). Client components read
128
+ * these from `src/data/business.ts`; the evidence stays in the server-only
129
+ * `src/data/businessEvidence.ts`, which declares the facts over these values with
130
+ * `defineBrandlabBusiness` (3.2.1).
131
+ */
132
+ export interface BrandlabBusinessFactValues {
133
+ identity: {
134
+ tradingName: string;
135
+ legalName?: string;
136
+ };
137
+ canonicalUrl: string;
138
+ contact?: {
139
+ phone?: BrandlabPhoneNumber;
140
+ email?: string;
141
+ };
142
+ location: {
143
+ model: BrandlabBusiness['location']['model'];
144
+ addressVisibility: BrandlabBusiness['location']['addressVisibility'];
145
+ address?: BrandlabPostalAddress;
146
+ serviceAreas?: readonly BrandlabServiceArea[];
147
+ };
148
+ openingHours?: readonly BrandlabOpeningHours[];
149
+ registration?: {
150
+ abn?: string;
151
+ licences?: readonly BrandlabLicence[];
152
+ };
153
+ externalEntities?: {
154
+ googlePlaceId?: string;
155
+ sameAs?: readonly string[];
156
+ };
157
+ }
125
158
  export declare function deriveBusinessFacts(business: BrandlabBusiness): BrandlabDerivedBusinessFacts;
159
+ /**
160
+ * What `deriveBusinessFacts` gives, from the display values alone (3.2.1), for
161
+ * the client-safe modules that derive links and labels from the facts. The values
162
+ * are checked as `defineBrandlabBusiness` checks the facts; proof references carry
163
+ * evidence, so they are never part of the display values (`proof` is empty).
164
+ */
165
+ export declare function deriveBusinessDisplayFacts(values: BrandlabBusinessFactValues): BrandlabDerivedBusinessFacts;
126
166
  //# sourceMappingURL=business.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"business.d.ts","sourceRoot":"","sources":["../src/business.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"business.d.ts","sourceRoot":"","sources":["../src/business.ts"],"names":[],"mappings":"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"}
package/dist/business.js CHANGED
@@ -275,56 +275,12 @@ export function defineBrandlabBusiness(business) {
275
275
  assertFact(business.contact.phone, 'contact.phone', assertPhone);
276
276
  assertFact(business.contact.email, 'contact.email', assertEmail);
277
277
  assertFact(business.location.address, 'location.address', assertAddress);
278
- assertFact(business.location.serviceAreas, 'location.serviceAreas', (areas) => {
279
- assertUniqueIds(areas, 'location.serviceAreas.value');
280
- areas.forEach((area, index) => {
281
- assertExactKeys(area, ['id', 'name', 'kind'], `location.serviceAreas.value[${index}]`);
282
- assertText(area.name, `location.serviceAreas.value[${index}].name`);
283
- if (area.name.trim() !== area.name || area.name.length > 160) {
284
- throw new Error(`location.serviceAreas.value[${index}].name must be bounded canonical text.`);
285
- }
286
- if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(area.id)) {
287
- throw new Error(`location.serviceAreas.value[${index}].id must be lowercase kebab-case.`);
288
- }
289
- if (!['city', 'region', 'suburb', 'state', 'country'].includes(area.kind)) {
290
- throw new Error(`location.serviceAreas.value[${index}].kind is invalid.`);
291
- }
292
- });
293
- });
294
- assertFact(business.openingHours, 'openingHours', (hours) => {
295
- const allDays = [];
296
- hours.forEach((period, index) => {
297
- assertExactKeys(period, ['days', 'opens', 'closes'], `openingHours.value[${index}]`);
298
- if (period.days.length === 0) {
299
- throw new Error(`openingHours.value[${index}].days cannot be empty.`);
300
- }
301
- assertUniqueStrings(period.days, `openingHours.value[${index}].days`);
302
- if (period.days.some((day) => !BRANDLAB_OPENING_HOURS_DAYS.has(day))) {
303
- throw new Error(`openingHours.value[${index}].days contains an invalid day.`);
304
- }
305
- allDays.push(...period.days);
306
- assertTime(period.opens, `openingHours.value[${index}].opens`);
307
- assertTime(period.closes, `openingHours.value[${index}].closes`);
308
- });
309
- assertUniqueStrings(allDays, 'openingHours.value.days');
310
- });
278
+ assertFact(business.location.serviceAreas, 'location.serviceAreas', assertServiceAreas);
279
+ assertFact(business.openingHours, 'openingHours', assertOpeningHours);
311
280
  assertFact(business.registration.abn, 'registration.abn', assertAbn);
312
- assertFact(business.registration.licences, 'registration.licences', (licences) => {
313
- assertUniqueStrings(licences.map((licence) => licence.identifier), 'registration.licences.value.identifiers');
314
- licences.forEach((licence, index) => {
315
- assertExactKeys(licence, ['name', 'identifier', 'jurisdiction'], `registration.licences.value[${index}]`);
316
- assertText(licence.name, `registration.licences.value[${index}].name`);
317
- assertText(licence.identifier, `registration.licences.value[${index}].identifier`);
318
- if (licence.jurisdiction !== undefined) {
319
- assertText(licence.jurisdiction, `registration.licences.value[${index}].jurisdiction`, 120);
320
- }
321
- });
322
- });
281
+ assertFact(business.registration.licences, 'registration.licences', assertLicences);
323
282
  assertFact(business.externalEntities.googlePlaceId, 'externalEntities.googlePlaceId');
324
- assertFact(business.externalEntities.sameAs, 'externalEntities.sameAs', (urls) => {
325
- assertUniqueStrings(urls, 'externalEntities.sameAs.value');
326
- urls.forEach((url, index) => assertAbsoluteHttpsUrl(url, `externalEntities.sameAs.value[${index}]`));
327
- });
283
+ assertFact(business.externalEntities.sameAs, 'externalEntities.sameAs', assertSameAs);
328
284
  if (business.proof) {
329
285
  assertFact(business.proof, 'proof', (proof) => {
330
286
  assertUniqueIds(proof, 'proof.value');
@@ -337,41 +293,119 @@ export function defineBrandlabBusiness(business) {
337
293
  });
338
294
  });
339
295
  }
340
- if (business.location.addressVisibility === 'not-applicable' &&
341
- business.location.address.status === 'verified') {
296
+ assertLocationModel({
297
+ model: business.location.model,
298
+ addressVisibility: business.location.addressVisibility,
299
+ address: verifiedValue(business.location.address),
300
+ serviceAreas: verifiedValue(business.location.serviceAreas),
301
+ });
302
+ return business;
303
+ }
304
+ function assertLocationModel(location) {
305
+ if (location.addressVisibility === 'not-applicable' && location.address) {
342
306
  throw new Error('A verified address cannot use addressVisibility "not-applicable".');
343
307
  }
344
- if (business.location.addressVisibility === 'public' &&
345
- business.location.address.status !== 'verified') {
308
+ if (location.addressVisibility === 'public' && !location.address) {
346
309
  throw new Error('A public address must have verified address facts.');
347
310
  }
348
- if (business.location.model === 'service-area' &&
349
- business.location.addressVisibility === 'public') {
311
+ if (location.model === 'service-area' && location.addressVisibility === 'public') {
350
312
  throw new Error('A service-area business cannot expose a public address.');
351
313
  }
352
- if (business.location.model === 'physical-location' &&
353
- business.location.addressVisibility !== 'public') {
314
+ if (location.model === 'physical-location' && location.addressVisibility !== 'public') {
354
315
  throw new Error('A physical-location business must expose its verified public address.');
355
316
  }
356
- if ((business.location.model === 'service-area' || business.location.model === 'hybrid') &&
357
- (business.location.serviceAreas.status !== 'verified' ||
358
- business.location.serviceAreas.value.length === 0)) {
317
+ if ((location.model === 'service-area' || location.model === 'hybrid') &&
318
+ (!location.serviceAreas || location.serviceAreas.length === 0)) {
359
319
  throw new Error('Service-area and hybrid businesses require verified service areas.');
360
320
  }
361
- return business;
321
+ }
322
+ function assertServiceAreas(areas) {
323
+ assertUniqueIds(areas, 'location.serviceAreas.value');
324
+ areas.forEach((area, index) => {
325
+ assertExactKeys(area, ['id', 'name', 'kind'], `location.serviceAreas.value[${index}]`);
326
+ assertText(area.name, `location.serviceAreas.value[${index}].name`);
327
+ if (area.name.trim() !== area.name || area.name.length > 160) {
328
+ throw new Error(`location.serviceAreas.value[${index}].name must be bounded canonical text.`);
329
+ }
330
+ if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(area.id)) {
331
+ throw new Error(`location.serviceAreas.value[${index}].id must be lowercase kebab-case.`);
332
+ }
333
+ if (!['city', 'region', 'suburb', 'state', 'country'].includes(area.kind)) {
334
+ throw new Error(`location.serviceAreas.value[${index}].kind is invalid.`);
335
+ }
336
+ });
337
+ }
338
+ function assertOpeningHours(hours) {
339
+ const allDays = [];
340
+ hours.forEach((period, index) => {
341
+ assertExactKeys(period, ['days', 'opens', 'closes'], `openingHours.value[${index}]`);
342
+ if (period.days.length === 0) {
343
+ throw new Error(`openingHours.value[${index}].days cannot be empty.`);
344
+ }
345
+ assertUniqueStrings(period.days, `openingHours.value[${index}].days`);
346
+ if (period.days.some((day) => !BRANDLAB_OPENING_HOURS_DAYS.has(day))) {
347
+ throw new Error(`openingHours.value[${index}].days contains an invalid day.`);
348
+ }
349
+ allDays.push(...period.days);
350
+ assertTime(period.opens, `openingHours.value[${index}].opens`);
351
+ assertTime(period.closes, `openingHours.value[${index}].closes`);
352
+ });
353
+ assertUniqueStrings(allDays, 'openingHours.value.days');
354
+ }
355
+ function assertLicences(licences) {
356
+ assertUniqueStrings(licences.map((licence) => licence.identifier), 'registration.licences.value.identifiers');
357
+ licences.forEach((licence, index) => {
358
+ assertExactKeys(licence, ['name', 'identifier', 'jurisdiction'], `registration.licences.value[${index}]`);
359
+ assertText(licence.name, `registration.licences.value[${index}].name`);
360
+ assertText(licence.identifier, `registration.licences.value[${index}].identifier`);
361
+ if (licence.jurisdiction !== undefined) {
362
+ assertText(licence.jurisdiction, `registration.licences.value[${index}].jurisdiction`, 120);
363
+ }
364
+ });
365
+ }
366
+ function assertSameAs(urls) {
367
+ assertUniqueStrings(urls, 'externalEntities.sameAs.value');
368
+ urls.forEach((url, index) => assertAbsoluteHttpsUrl(url, `externalEntities.sameAs.value[${index}]`));
362
369
  }
363
370
  function verifiedValue(fact) {
364
371
  return fact.status === 'verified' ? fact.value : undefined;
365
372
  }
366
- export function deriveBusinessFacts(business) {
367
- defineBrandlabBusiness(business);
368
- const canonicalUrl = new URL(business.canonicalUrl.value).origin;
369
- const phone = verifiedValue(business.contact.phone);
370
- const address = verifiedValue(business.location.address);
373
+ function factValues(business) {
374
+ return {
375
+ identity: {
376
+ tradingName: business.identity.tradingName.value,
377
+ ...(verifiedValue(business.identity.legalName) !== undefined ? { legalName: verifiedValue(business.identity.legalName) } : {}),
378
+ },
379
+ canonicalUrl: business.canonicalUrl.value,
380
+ contact: {
381
+ ...(verifiedValue(business.contact.phone) ? { phone: verifiedValue(business.contact.phone) } : {}),
382
+ ...(verifiedValue(business.contact.email) !== undefined ? { email: verifiedValue(business.contact.email) } : {}),
383
+ },
384
+ location: {
385
+ model: business.location.model,
386
+ addressVisibility: business.location.addressVisibility,
387
+ ...(verifiedValue(business.location.address) ? { address: verifiedValue(business.location.address) } : {}),
388
+ ...(verifiedValue(business.location.serviceAreas) ? { serviceAreas: verifiedValue(business.location.serviceAreas) } : {}),
389
+ },
390
+ ...(verifiedValue(business.openingHours) ? { openingHours: verifiedValue(business.openingHours) } : {}),
391
+ registration: {
392
+ ...(verifiedValue(business.registration.abn) !== undefined ? { abn: verifiedValue(business.registration.abn) } : {}),
393
+ ...(verifiedValue(business.registration.licences) ? { licences: verifiedValue(business.registration.licences) } : {}),
394
+ },
395
+ externalEntities: {
396
+ ...(verifiedValue(business.externalEntities.googlePlaceId) !== undefined ? { googlePlaceId: verifiedValue(business.externalEntities.googlePlaceId) } : {}),
397
+ ...(verifiedValue(business.externalEntities.sameAs) ? { sameAs: verifiedValue(business.externalEntities.sameAs) } : {}),
398
+ },
399
+ };
400
+ }
401
+ function deriveFromValues(values) {
402
+ const canonicalUrl = new URL(values.canonicalUrl).origin;
403
+ const phone = values.contact?.phone;
404
+ const address = values.location.address;
371
405
  return {
372
- tradingName: business.identity.tradingName.value,
373
- ...(business.identity.legalName.status === 'verified'
374
- ? { legalName: business.identity.legalName.value }
406
+ tradingName: values.identity.tradingName,
407
+ ...(values.identity.legalName !== undefined
408
+ ? { legalName: values.identity.legalName }
375
409
  : {}),
376
410
  canonicalUrl,
377
411
  canonicalDomain: new URL(canonicalUrl).hostname,
@@ -387,23 +421,85 @@ export function deriveBusinessFacts(business) {
387
421
  },
388
422
  }
389
423
  : {}),
390
- ...(business.contact.email.status === 'verified'
391
- ? { email: business.contact.email.value.trim().toLowerCase() }
424
+ ...(values.contact?.email !== undefined
425
+ ? { email: values.contact.email.trim().toLowerCase() }
392
426
  : {}),
393
- ...(business.location.addressVisibility === 'public' && address
427
+ ...(values.location.addressVisibility === 'public' && address
394
428
  ? { publicAddress: address }
395
429
  : {}),
396
- serviceAreas: verifiedValue(business.location.serviceAreas) ?? [],
397
- openingHours: verifiedValue(business.openingHours) ?? [],
398
- ...(business.registration.abn.status === 'verified'
399
- ? { abn: normalizeAbn(business.registration.abn.value) }
430
+ serviceAreas: values.location.serviceAreas ?? [],
431
+ openingHours: values.openingHours ?? [],
432
+ ...(values.registration?.abn !== undefined
433
+ ? { abn: normalizeAbn(values.registration.abn) }
400
434
  : {}),
401
- licences: verifiedValue(business.registration.licences) ?? [],
402
- ...(business.externalEntities.googlePlaceId.status === 'verified'
403
- ? { googlePlaceId: business.externalEntities.googlePlaceId.value }
435
+ licences: values.registration?.licences ?? [],
436
+ ...(values.externalEntities?.googlePlaceId !== undefined
437
+ ? { googlePlaceId: values.externalEntities.googlePlaceId }
404
438
  : {}),
405
- sameAs: verifiedValue(business.externalEntities.sameAs) ?? [],
439
+ sameAs: values.externalEntities?.sameAs ?? [],
440
+ };
441
+ }
442
+ export function deriveBusinessFacts(business) {
443
+ defineBrandlabBusiness(business);
444
+ return {
445
+ ...deriveFromValues(factValues(business)),
406
446
  proof: business.proof ? verifiedValue(business.proof) ?? [] : [],
407
447
  };
408
448
  }
449
+ /**
450
+ * What `deriveBusinessFacts` gives, from the display values alone (3.2.1), for
451
+ * the client-safe modules that derive links and labels from the facts. The values
452
+ * are checked as `defineBrandlabBusiness` checks the facts; proof references carry
453
+ * evidence, so they are never part of the display values (`proof` is empty).
454
+ */
455
+ export function deriveBusinessDisplayFacts(values) {
456
+ assertBusinessFactValues(values);
457
+ return { ...deriveFromValues(values), proof: [] };
458
+ }
459
+ function assertBusinessFactValues(values) {
460
+ if (!values || typeof values !== 'object')
461
+ throw new Error('The business fact values must be an object.');
462
+ assertExactKeys(values, ['identity', 'canonicalUrl', 'contact', 'location', 'openingHours', 'registration', 'externalEntities'], 'business values');
463
+ assertExactKeys(values.identity, ['tradingName', 'legalName'], 'identity');
464
+ assertText(values.identity.tradingName, 'identity.tradingName.value');
465
+ if (values.identity.legalName !== undefined)
466
+ assertText(values.identity.legalName, 'identity.legalName.value');
467
+ assertText(values.canonicalUrl, 'canonicalUrl.value');
468
+ assertAbsoluteHttpsUrl(values.canonicalUrl, 'canonicalUrl.value', true);
469
+ if (values.contact !== undefined) {
470
+ assertExactKeys(values.contact, ['phone', 'email'], 'contact');
471
+ if (values.contact.phone !== undefined)
472
+ assertPhone(values.contact.phone);
473
+ if (values.contact.email !== undefined)
474
+ assertEmail(values.contact.email);
475
+ }
476
+ assertExactKeys(values.location, ['model', 'addressVisibility', 'address', 'serviceAreas'], 'location');
477
+ if (!['service-area', 'physical-location', 'hybrid'].includes(values.location.model)) {
478
+ throw new Error('location.model is invalid.');
479
+ }
480
+ if (!['public', 'hidden', 'not-applicable'].includes(values.location.addressVisibility)) {
481
+ throw new Error('location.addressVisibility is invalid.');
482
+ }
483
+ if (values.location.address !== undefined)
484
+ assertAddress(values.location.address);
485
+ if (values.location.serviceAreas !== undefined)
486
+ assertServiceAreas(values.location.serviceAreas);
487
+ if (values.openingHours !== undefined)
488
+ assertOpeningHours(values.openingHours);
489
+ if (values.registration !== undefined) {
490
+ assertExactKeys(values.registration, ['abn', 'licences'], 'registration');
491
+ if (values.registration.abn !== undefined)
492
+ assertAbn(values.registration.abn);
493
+ if (values.registration.licences !== undefined)
494
+ assertLicences(values.registration.licences);
495
+ }
496
+ if (values.externalEntities !== undefined) {
497
+ assertExactKeys(values.externalEntities, ['googlePlaceId', 'sameAs'], 'externalEntities');
498
+ if (values.externalEntities.googlePlaceId !== undefined)
499
+ assertText(values.externalEntities.googlePlaceId, 'externalEntities.googlePlaceId.value');
500
+ if (values.externalEntities.sameAs !== undefined)
501
+ assertSameAs(values.externalEntities.sameAs);
502
+ }
503
+ assertLocationModel(values.location);
504
+ }
409
505
  //# sourceMappingURL=business.js.map