@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.
- package/dist/analytics.d.ts +9 -2
- package/dist/analytics.d.ts.map +1 -1
- package/dist/analytics.js +3 -2
- package/dist/analytics.js.map +1 -1
- package/dist/business.d.ts +40 -0
- package/dist/business.d.ts.map +1 -1
- package/dist/business.js +175 -79
- package/dist/business.js.map +1 -1
- package/dist/geography.d.ts +2 -0
- package/dist/geography.d.ts.map +1 -1
- package/dist/geography.js +0 -18
- package/dist/geography.js.map +1 -1
- package/dist/google-reviews.d.ts +3 -1
- package/dist/google-reviews.d.ts.map +1 -1
- package/dist/google-reviews.js +1 -0
- package/dist/google-reviews.js.map +1 -1
- package/dist/site.d.ts +11 -1
- package/dist/site.d.ts.map +1 -1
- package/dist/site.js +24 -15
- package/dist/site.js.map +1 -1
- package/package.json +1 -1
package/dist/analytics.d.ts
CHANGED
|
@@ -35,13 +35,20 @@ export interface BrandlabAnalyticsContract {
|
|
|
35
35
|
websiteCalls?: {
|
|
36
36
|
conversionLabelEnv: string;
|
|
37
37
|
};
|
|
38
|
-
/**
|
|
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:
|
|
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;
|
package/dist/analytics.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../src/analytics.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,qBAAqB,2BAA4B,CAAA;AAC9D,eAAO,MAAM,kCAAkC,+BAAgC,CAAA;AAC/E,eAAO,MAAM,+BAA+B,mEAIlC,CAAA;AAEV,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAA;AACvE,MAAM,MAAM,8BAA8B,GACxC,CAAC,OAAO,kCAAkC,CAAC,CAAC,MAAM,CAAC,CAAA;AACrD,MAAM,MAAM,2BAA2B,GACrC,CAAC,OAAO,+BAA+B,CAAC,CAAC,MAAM,CAAC,CAAA;AAElD,MAAM,MAAM,gCAAgC,GACxC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAChC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAClE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GACxD;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEjC,MAAM,WAAW,8BAA8B;IAC7C,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,SAAS,gCAAgC,EAAE,CAAA;CACxD;AAED,MAAM,WAAW,yBAAyB;IACxC,yFAAyF;IACzF,SAAS,EAAE,MAAM,CAAA;IACjB,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,CAAC,EAAE;QACV,eAAe,EAAE,MAAM,CAAA;QACvB,yBAAyB,EAAE,MAAM,CAAA;QACjC,6EAA6E;QAC7E,YAAY,CAAC,EAAE;YACb,kBAAkB,EAAE,MAAM,CAAA;SAC3B,CAAA;QACD
|
|
1
|
+
{"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../src/analytics.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,qBAAqB,2BAA4B,CAAA;AAC9D,eAAO,MAAM,kCAAkC,+BAAgC,CAAA;AAC/E,eAAO,MAAM,+BAA+B,mEAIlC,CAAA;AAEV,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAA;AACvE,MAAM,MAAM,8BAA8B,GACxC,CAAC,OAAO,kCAAkC,CAAC,CAAC,MAAM,CAAC,CAAA;AACrD,MAAM,MAAM,2BAA2B,GACrC,CAAC,OAAO,+BAA+B,CAAC,CAAC,MAAM,CAAC,CAAA;AAElD,MAAM,MAAM,gCAAgC,GACxC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAChC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAClE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GACxD;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEjC,MAAM,WAAW,8BAA8B;IAC7C,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,SAAS,gCAAgC,EAAE,CAAA;CACxD;AAED,MAAM,WAAW,yBAAyB;IACxC,yFAAyF;IACzF,SAAS,EAAE,MAAM,CAAA;IACjB,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,CAAC,EAAE;QACV,eAAe,EAAE,MAAM,CAAA;QACvB,yBAAyB,EAAE,MAAM,CAAA;QACjC,6EAA6E;QAC7E,YAAY,CAAC,EAAE;YACb,kBAAkB,EAAE,MAAM,CAAA;SAC3B,CAAA;QACD;;;;;;WAMG;QACH,mBAAmB,CAAC,EAAE;YAAE,IAAI,EAAE,+BAA+B,CAAA;SAAE,CAAA;KAChE,CAAA;IACD,gBAAgB,CAAC,EAAE,SAAS,8BAA8B,EAAE,CAAA;CAC7D;AACD,MAAM,MAAM,+BAA+B,GAAG,kBAAkB,GAAG,WAAW,CAAA;AAE9E,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,IAAI,EAChC,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAC1C,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxC,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GAC5C,CAAC,IAAI,EAAE,IAAI,CAAC,KACX,IAAI,CAAA;AAET,MAAM,WAAW,0BAA0B;IACzC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB;AAED,MAAM,WAAW,mCAAoC,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClF,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,OAAO,CAAA;IAChB,sBAAsB,EAAE,OAAO,CAAA;IAC/B,iBAAiB,EAAE,OAAO,CAAA;CAC3B;AA6DD,wBAAgB,uBAAuB,CAAC,KAAK,CAAC,CAAC,SAAS,yBAAyB,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CA0GjG;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,kBAAkB,CAE/E;AAED,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,MAAM,GACZ,KAAK,IAAI,8BAA8B,CAIzC;AAED,wBAAgB,8BAA8B,CAAC,YAAY,EAAE,MAAM,UAGlE;AAMD,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,sBAgBhF;AAED,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,sBAShF;AAED,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,0BAA0B,GAAG,IAAI,GAAG,SAAS,GACtD,mCAAmC,GAAG,SAAS,CAcjD;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE;IACjD,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAChC,sBASA;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE;IACrD,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAChC,sBAEA;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE;IAClD,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,IAAI,CAAC,EAAE,aAAa,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAChC,WAWA;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,2BAmC3E;AA2BD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE;IACjD,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,wBAAwB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxC,IAAI,CAAC,EAAE,aAAa,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,kBAAkB,CAAC,EAAE,0BAA0B,GAAG,IAAI,CAAA;IACtD,0BAA0B,CAAC,EAAE,OAAO,CAAA;IACpC,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACpC,GAAG,2BAA2B,CA0D9B"}
|
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
|
-
|
|
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 ?? [];
|
package/dist/analytics.js.map
CHANGED
|
@@ -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"]}
|
package/dist/business.d.ts
CHANGED
|
@@ -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
|
package/dist/business.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"business.d.ts","sourceRoot":"","sources":["../src/business.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,SAAS,GAAG,gBAAgB,CAAA;AAEzE,eAAO,MAAM,8BAA8B,sMAUjC,CAAA;AAEV,MAAM,MAAM,0BAA0B,GACpC,CAAC,OAAO,8BAA8B,CAAC,CAAC,MAAM,CAAC,CAAA;AAEjD,eAAO,MAAM,0BAA0B,gGAQ7B,CAAA;AAEV,MAAM,MAAM,uBAAuB,GACjC,CAAC,OAAO,0BAA0B,CAAC,CAAC,MAAM,CAAC,CAAA;AAE7C,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,0BAA0B,CAAA;IACtC,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,eAAe,EAAE,SAAS,uBAAuB,EAAE,CAAA;CACpD;AAED,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,MAAM,EAAE,UAAU,CAAA;IAClB,KAAK,EAAE,CAAC,CAAA;IACR,QAAQ,EAAE,iBAAiB,CAAA;CAC5B;AAED,MAAM,MAAM,YAAY,CAAC,CAAC,IACtB,YAAY,CAAC,CAAC,CAAC,GACf;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB;IAAE,MAAM,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhD,MAAM,WAAW,mBAAmB;IAClC,8FAA8F;IAC9F,OAAO,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB,cAAc,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAA;CACzD;AAED,MAAM,MAAM,uBAAuB,GAC/B,QAAQ,GACR,SAAS,GACT,WAAW,GACX,UAAU,GACV,QAAQ,GACR,UAAU,GACV,QAAQ,CAAA;AAEZ,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,SAAS,uBAAuB,EAAE,CAAA;IACxC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,eAAe,GAAG,SAAS,GAAG,iBAAiB,CAAA;IAC5E,QAAQ,EAAE,iBAAiB,CAAA;CAC5B;AAmBD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE;QACR,WAAW,EAAE,YAAY,CAAC,MAAM,CAAC,GAAG;YAAE,MAAM,EAAE,UAAU,CAAA;SAAE,CAAA;QAC1D,SAAS,EAAE,YAAY,CAAC,MAAM,CAAC,CAAA;KAChC,CAAA;IACD,YAAY,EAAE,YAAY,CAAC,MAAM,CAAC,GAAG;QAAE,MAAM,EAAE,UAAU,CAAA;KAAE,CAAA;IAC3D,OAAO,EAAE;QACP,KAAK,EAAE,YAAY,CAAC,mBAAmB,CAAC,CAAA;QACxC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,CAAA;KAC5B,CAAA;IACD,QAAQ,EAAE;QACR,KAAK,EAAE,cAAc,GAAG,mBAAmB,GAAG,QAAQ,CAAA;QACtD,iBAAiB,EAAE,QAAQ,GAAG,QAAQ,GAAG,gBAAgB,CAAA;QACzD,OAAO,EAAE,YAAY,CAAC,qBAAqB,CAAC,CAAA;QAC5C,YAAY,EAAE,YAAY,CAAC,SAAS,mBAAmB,EAAE,CAAC,CAAA;KAC3D,CAAA;IACD,YAAY,EAAE,YAAY,CAAC,SAAS,oBAAoB,EAAE,CAAC,CAAA;IAC3D,YAAY,EAAE;QACZ,GAAG,EAAE,YAAY,CAAC,MAAM,CAAC,CAAA;QACzB,QAAQ,EAAE,YAAY,CAAC,SAAS,eAAe,EAAE,CAAC,CAAA;KACnD,CAAA;IACD,gBAAgB,EAAE;QAChB,aAAa,EAAE,YAAY,CAAC,MAAM,CAAC,CAAA;QACnC,MAAM,EAAE,YAAY,CAAC,SAAS,MAAM,EAAE,CAAC,CAAA;KACxC,CAAA;IACD,qFAAqF;IACrF,KAAK,CAAC,EAAE,YAAY,CAAC,SAAS,sBAAsB,EAAE,CAAC,CAAA;CACxD;AAED,MAAM,WAAW,4BAA4B;IAC3C,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,eAAe,EAAE,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,mBAAmB,GAAG;QAC5B,MAAM,EAAE,MAAM,CAAA;QACd;;;WAGG;QACH,aAAa,EAAE,OAAO,CAAA;QACtB,sHAAsH;QACtH,OAAO,EAAE,MAAM,CAAA;QACf,oHAAoH;QACpH,eAAe,EAAE,MAAM,CAAA;KACxB,CAAA;IACD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,qBAAqB,CAAA;IACrC,YAAY,EAAE,SAAS,mBAAmB,EAAE,CAAA;IAC5C,YAAY,EAAE,SAAS,oBAAoB,EAAE,CAAA;IAC7C,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAA;IACpC,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;IACzB,KAAK,EAAE,SAAS,sBAAsB,EAAE,CAAA;CACzC;AAED,wBAAgB,YAAY,CAAC,CAAC,EAC5B,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,iBAAiB,GAC1B,YAAY,CAAC,CAAC,CAAC,CAMjB;AAED,wBAAgB,WAAW,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC,CAAC,CAEhD;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,CAEpE;AAED,wBAAgB,6BAA6B,CAAC,CAAC,EAC7C,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,EACrB,OAAO,EAAE,uBAAuB,EAChC,IAAI,GAAE,IAAiB,GACtB,CAAC,GAAG,SAAS,CAUf;AA6PD,wBAAgB,sBAAsB,CAAC,KAAK,CAAC,CAAC,SAAS,gBAAgB,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"business.d.ts","sourceRoot":"","sources":["../src/business.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,SAAS,GAAG,gBAAgB,CAAA;AAEzE,eAAO,MAAM,8BAA8B,sMAUjC,CAAA;AAEV,MAAM,MAAM,0BAA0B,GACpC,CAAC,OAAO,8BAA8B,CAAC,CAAC,MAAM,CAAC,CAAA;AAEjD,eAAO,MAAM,0BAA0B,gGAQ7B,CAAA;AAEV,MAAM,MAAM,uBAAuB,GACjC,CAAC,OAAO,0BAA0B,CAAC,CAAC,MAAM,CAAC,CAAA;AAE7C,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,0BAA0B,CAAA;IACtC,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,eAAe,EAAE,SAAS,uBAAuB,EAAE,CAAA;CACpD;AAED,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,MAAM,EAAE,UAAU,CAAA;IAClB,KAAK,EAAE,CAAC,CAAA;IACR,QAAQ,EAAE,iBAAiB,CAAA;CAC5B;AAED,MAAM,MAAM,YAAY,CAAC,CAAC,IACtB,YAAY,CAAC,CAAC,CAAC,GACf;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB;IAAE,MAAM,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhD,MAAM,WAAW,mBAAmB;IAClC,8FAA8F;IAC9F,OAAO,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB,cAAc,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAA;CACzD;AAED,MAAM,MAAM,uBAAuB,GAC/B,QAAQ,GACR,SAAS,GACT,WAAW,GACX,UAAU,GACV,QAAQ,GACR,UAAU,GACV,QAAQ,CAAA;AAEZ,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,SAAS,uBAAuB,EAAE,CAAA;IACxC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,eAAe,GAAG,SAAS,GAAG,iBAAiB,CAAA;IAC5E,QAAQ,EAAE,iBAAiB,CAAA;CAC5B;AAmBD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE;QACR,WAAW,EAAE,YAAY,CAAC,MAAM,CAAC,GAAG;YAAE,MAAM,EAAE,UAAU,CAAA;SAAE,CAAA;QAC1D,SAAS,EAAE,YAAY,CAAC,MAAM,CAAC,CAAA;KAChC,CAAA;IACD,YAAY,EAAE,YAAY,CAAC,MAAM,CAAC,GAAG;QAAE,MAAM,EAAE,UAAU,CAAA;KAAE,CAAA;IAC3D,OAAO,EAAE;QACP,KAAK,EAAE,YAAY,CAAC,mBAAmB,CAAC,CAAA;QACxC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,CAAA;KAC5B,CAAA;IACD,QAAQ,EAAE;QACR,KAAK,EAAE,cAAc,GAAG,mBAAmB,GAAG,QAAQ,CAAA;QACtD,iBAAiB,EAAE,QAAQ,GAAG,QAAQ,GAAG,gBAAgB,CAAA;QACzD,OAAO,EAAE,YAAY,CAAC,qBAAqB,CAAC,CAAA;QAC5C,YAAY,EAAE,YAAY,CAAC,SAAS,mBAAmB,EAAE,CAAC,CAAA;KAC3D,CAAA;IACD,YAAY,EAAE,YAAY,CAAC,SAAS,oBAAoB,EAAE,CAAC,CAAA;IAC3D,YAAY,EAAE;QACZ,GAAG,EAAE,YAAY,CAAC,MAAM,CAAC,CAAA;QACzB,QAAQ,EAAE,YAAY,CAAC,SAAS,eAAe,EAAE,CAAC,CAAA;KACnD,CAAA;IACD,gBAAgB,EAAE;QAChB,aAAa,EAAE,YAAY,CAAC,MAAM,CAAC,CAAA;QACnC,MAAM,EAAE,YAAY,CAAC,SAAS,MAAM,EAAE,CAAC,CAAA;KACxC,CAAA;IACD,qFAAqF;IACrF,KAAK,CAAC,EAAE,YAAY,CAAC,SAAS,sBAAsB,EAAE,CAAC,CAAA;CACxD;AAED,MAAM,WAAW,4BAA4B;IAC3C,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,eAAe,EAAE,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,mBAAmB,GAAG;QAC5B,MAAM,EAAE,MAAM,CAAA;QACd;;;WAGG;QACH,aAAa,EAAE,OAAO,CAAA;QACtB,sHAAsH;QACtH,OAAO,EAAE,MAAM,CAAA;QACf,oHAAoH;QACpH,eAAe,EAAE,MAAM,CAAA;KACxB,CAAA;IACD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,qBAAqB,CAAA;IACrC,YAAY,EAAE,SAAS,mBAAmB,EAAE,CAAA;IAC5C,YAAY,EAAE,SAAS,oBAAoB,EAAE,CAAA;IAC7C,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAA;IACpC,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;IACzB,KAAK,EAAE,SAAS,sBAAsB,EAAE,CAAA;CACzC;AAED,wBAAgB,YAAY,CAAC,CAAC,EAC5B,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,iBAAiB,GAC1B,YAAY,CAAC,CAAC,CAAC,CAMjB;AAED,wBAAgB,WAAW,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC,CAAC,CAEhD;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,CAEpE;AAED,wBAAgB,6BAA6B,CAAC,CAAC,EAC7C,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,EACrB,OAAO,EAAE,uBAAuB,EAChC,IAAI,GAAE,IAAiB,GACtB,CAAC,GAAG,SAAS,CAUf;AA6PD,wBAAgB,sBAAsB,CAAC,KAAK,CAAC,CAAC,SAAS,gBAAgB,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CA4EvF;AAoGD;;;;;;GAMG;AACH,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACrD,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,mBAAmB,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACzD,QAAQ,EAAE;QACR,KAAK,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,CAAA;QAC5C,iBAAiB,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAC,mBAAmB,CAAC,CAAA;QACpE,OAAO,CAAC,EAAE,qBAAqB,CAAA;QAC/B,YAAY,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAA;KAC9C,CAAA;IACD,YAAY,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAA;IAC9C,YAAY,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,SAAS,eAAe,EAAE,CAAA;KAAE,CAAA;IACtE,gBAAgB,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAA;CAC1E;AA0ED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,4BAA4B,CAM5F;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,0BAA0B,GAAG,4BAA4B,CAG3G"}
|
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',
|
|
279
|
-
|
|
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',
|
|
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',
|
|
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
|
-
|
|
341
|
-
business.location.
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 ((
|
|
357
|
-
(
|
|
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
|
-
|
|
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
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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:
|
|
373
|
-
...(
|
|
374
|
-
? { legalName:
|
|
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
|
-
...(
|
|
391
|
-
? { email:
|
|
424
|
+
...(values.contact?.email !== undefined
|
|
425
|
+
? { email: values.contact.email.trim().toLowerCase() }
|
|
392
426
|
: {}),
|
|
393
|
-
...(
|
|
427
|
+
...(values.location.addressVisibility === 'public' && address
|
|
394
428
|
? { publicAddress: address }
|
|
395
429
|
: {}),
|
|
396
|
-
serviceAreas:
|
|
397
|
-
openingHours:
|
|
398
|
-
...(
|
|
399
|
-
? { abn: normalizeAbn(
|
|
430
|
+
serviceAreas: values.location.serviceAreas ?? [],
|
|
431
|
+
openingHours: values.openingHours ?? [],
|
|
432
|
+
...(values.registration?.abn !== undefined
|
|
433
|
+
? { abn: normalizeAbn(values.registration.abn) }
|
|
400
434
|
: {}),
|
|
401
|
-
licences:
|
|
402
|
-
...(
|
|
403
|
-
? { googlePlaceId:
|
|
435
|
+
licences: values.registration?.licences ?? [],
|
|
436
|
+
...(values.externalEntities?.googlePlaceId !== undefined
|
|
437
|
+
? { googlePlaceId: values.externalEntities.googlePlaceId }
|
|
404
438
|
: {}),
|
|
405
|
-
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
|