@brandlab-digital/site-core 1.1.0 → 1.2.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/contact.d.ts +9 -2
- package/dist/contact.d.ts.map +1 -1
- package/dist/contact.js +8 -1
- package/dist/contact.js.map +1 -1
- package/dist/project.d.ts +1 -1
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +1 -0
- package/dist/project.js.map +1 -1
- package/package.json +1 -1
package/dist/contact.d.ts
CHANGED
|
@@ -4,19 +4,26 @@ export interface ContactAttachmentPolicy {
|
|
|
4
4
|
maxTotalSizeBytes: number;
|
|
5
5
|
allowedTypes: readonly ContactAttachmentMimeType[];
|
|
6
6
|
}
|
|
7
|
-
export type ContactAttachmentMimeType = 'image/jpeg' | 'image/jpg' | 'image/png' | 'image/webp' | 'image/gif' | 'image/heic' | 'image/heif';
|
|
7
|
+
export type ContactAttachmentMimeType = 'application/pdf' | 'image/jpeg' | 'image/jpg' | 'image/png' | 'image/webp' | 'image/gif' | 'image/heic' | 'image/heif';
|
|
8
8
|
export declare const DEFAULT_CONTACT_ATTACHMENT_POLICY: {
|
|
9
9
|
readonly maxFiles: 5;
|
|
10
10
|
readonly maxFileSizeBytes: 4000000;
|
|
11
11
|
readonly maxTotalSizeBytes: 4000000;
|
|
12
12
|
readonly allowedTypes: readonly ["image/jpeg", "image/jpg", "image/png", "image/webp", "image/gif", "image/heic", "image/heif"];
|
|
13
13
|
};
|
|
14
|
+
export declare const DOCUMENT_CONTACT_ATTACHMENT_POLICY: {
|
|
15
|
+
readonly allowedTypes: readonly ["image/jpeg", "image/jpg", "image/png", "image/webp", "image/gif", "image/heic", "image/heif", "application/pdf"];
|
|
16
|
+
readonly maxFiles: 5;
|
|
17
|
+
readonly maxFileSizeBytes: 4000000;
|
|
18
|
+
readonly maxTotalSizeBytes: 4000000;
|
|
19
|
+
};
|
|
14
20
|
export declare function contactAttachmentAccept(policy?: ContactAttachmentPolicy): string;
|
|
15
21
|
export declare function contactAttachmentTypeLabel(policy?: ContactAttachmentPolicy): string;
|
|
16
22
|
export declare function isAllowedContactAttachmentType(value: string, policy?: ContactAttachmentPolicy): value is ContactAttachmentMimeType;
|
|
17
23
|
export declare function resolveContactAttachmentType(filename: string, fileType?: string, policy?: ContactAttachmentPolicy): ContactAttachmentMimeType | null;
|
|
18
24
|
export declare function createContactAttachmentFilename(options: {
|
|
19
25
|
index: number;
|
|
26
|
+
contentType?: string;
|
|
20
27
|
}): string;
|
|
21
28
|
export declare function validateContactAttachment(file: {
|
|
22
29
|
filename: string;
|
|
@@ -129,7 +136,7 @@ export interface BrandlabContactContract {
|
|
|
129
136
|
fieldProfile: string;
|
|
130
137
|
fields: readonly ContactFieldContract[];
|
|
131
138
|
attachments: {
|
|
132
|
-
mode: 'none' | 'images-optional' | 'images-required';
|
|
139
|
+
mode: 'none' | 'images-optional' | 'images-required' | 'images-and-pdfs-optional';
|
|
133
140
|
};
|
|
134
141
|
delivery: {
|
|
135
142
|
recipientEnv: string;
|
package/dist/contact.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact.d.ts","sourceRoot":"","sources":["../src/contact.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAA;IAChB,gBAAgB,EAAE,MAAM,CAAA;IACxB,iBAAiB,EAAE,MAAM,CAAA;IACzB,YAAY,EAAE,SAAS,yBAAyB,EAAE,CAAA;CACnD;AAED,MAAM,MAAM,yBAAyB,GACjC,YAAY,GACZ,WAAW,GACX,WAAW,GACX,YAAY,GACZ,WAAW,GACX,YAAY,GACZ,YAAY,CAAA;AAEhB,eAAO,MAAM,iCAAiC;;;;;CAaF,CAAA;
|
|
1
|
+
{"version":3,"file":"contact.d.ts","sourceRoot":"","sources":["../src/contact.ts"],"names":[],"mappings":"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"}
|
package/dist/contact.js
CHANGED
|
@@ -14,7 +14,12 @@ export const DEFAULT_CONTACT_ATTACHMENT_POLICY = {
|
|
|
14
14
|
'image/heif',
|
|
15
15
|
],
|
|
16
16
|
};
|
|
17
|
+
export const DOCUMENT_CONTACT_ATTACHMENT_POLICY = {
|
|
18
|
+
...DEFAULT_CONTACT_ATTACHMENT_POLICY,
|
|
19
|
+
allowedTypes: [...DEFAULT_CONTACT_ATTACHMENT_POLICY.allowedTypes, 'application/pdf'],
|
|
20
|
+
};
|
|
17
21
|
const EXTENSION_TO_MIME = {
|
|
22
|
+
pdf: 'application/pdf',
|
|
18
23
|
jpg: 'image/jpeg',
|
|
19
24
|
jpeg: 'image/jpeg',
|
|
20
25
|
png: 'image/png',
|
|
@@ -56,6 +61,8 @@ export function resolveContactAttachmentType(filename, fileType, policy = DEFAUL
|
|
|
56
61
|
return mappedType && isAllowedContactAttachmentType(mappedType, policy) ? mappedType : null;
|
|
57
62
|
}
|
|
58
63
|
export function createContactAttachmentFilename(options) {
|
|
64
|
+
if (options.contentType === 'application/pdf')
|
|
65
|
+
return `document-${options.index + 1}.pdf`;
|
|
59
66
|
return `image-${options.index + 1}.jpg`;
|
|
60
67
|
}
|
|
61
68
|
export function validateContactAttachment(file, policy = DEFAULT_CONTACT_ATTACHMENT_POLICY) {
|
|
@@ -338,7 +345,7 @@ export function defineContactContract(contract) {
|
|
|
338
345
|
assertContactContractKeys(contract, ['projectId', 'fieldProfile', 'fields', 'attachments', 'delivery'], 'Contact contract');
|
|
339
346
|
if (!contract.attachments ||
|
|
340
347
|
typeof contract.attachments !== 'object' ||
|
|
341
|
-
!['none', 'images-optional', 'images-required'].includes(contract.attachments.mode)) {
|
|
348
|
+
!['none', 'images-optional', 'images-required', 'images-and-pdfs-optional'].includes(contract.attachments.mode)) {
|
|
342
349
|
throw new Error('Contact contract attachment mode is invalid.');
|
|
343
350
|
}
|
|
344
351
|
assertContactContractKeys(contract.attachments, ['mode'], 'Contact contract attachments');
|
package/dist/contact.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact.js","sourceRoot":"","sources":["../src/contact.ts"],"names":[],"mappings":"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","sourcesContent":["import { normalizeBrandlabRoutePath } from './routes.js'\nimport { assertBrandlabCanonicalEntityId } from './entity-id.js'\n\nexport interface ContactAttachmentPolicy {\n maxFiles: number\n maxFileSizeBytes: number\n maxTotalSizeBytes: number\n allowedTypes: readonly ContactAttachmentMimeType[]\n}\n\nexport type ContactAttachmentMimeType =\n | 'image/jpeg'\n | 'image/jpg'\n | 'image/png'\n | 'image/webp'\n | 'image/gif'\n | 'image/heic'\n | 'image/heif'\n\nexport const DEFAULT_CONTACT_ATTACHMENT_POLICY = {\n maxFiles: 5,\n maxFileSizeBytes: 4_000_000,\n maxTotalSizeBytes: 4_000_000,\n allowedTypes: [\n 'image/jpeg',\n 'image/jpg',\n 'image/png',\n 'image/webp',\n 'image/gif',\n 'image/heic',\n 'image/heif',\n ],\n} as const satisfies ContactAttachmentPolicy\n\nconst EXTENSION_TO_MIME: Record<string, ContactAttachmentMimeType> = {\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n gif: 'image/gif',\n webp: 'image/webp',\n heic: 'image/heic',\n heif: 'image/heif',\n}\n\nexport function contactAttachmentAccept(\n policy: ContactAttachmentPolicy = DEFAULT_CONTACT_ATTACHMENT_POLICY,\n) {\n return [...new Set(policy.allowedTypes)].join(',')\n}\n\nexport function contactAttachmentTypeLabel(\n policy: ContactAttachmentPolicy = DEFAULT_CONTACT_ATTACHMENT_POLICY,\n) {\n const labels = policy.allowedTypes\n .map((type) => {\n const subtype = type.split('/')[1] ?? type\n if (subtype === 'jpeg' || subtype === 'jpg') return 'JPG'\n if (subtype === 'webp') return 'WebP'\n return subtype.toUpperCase()\n })\n .filter((label, index, labels) => labels.indexOf(label) === index)\n\n if (labels.length <= 1) return labels[0] ?? ''\n return `${labels.slice(0, -1).join(', ')}, or ${labels[labels.length - 1]}`\n}\n\nexport function isAllowedContactAttachmentType(\n value: string,\n policy: ContactAttachmentPolicy = DEFAULT_CONTACT_ATTACHMENT_POLICY,\n): value is ContactAttachmentMimeType {\n return policy.allowedTypes.includes(value as ContactAttachmentMimeType)\n}\n\nexport function resolveContactAttachmentType(\n filename: string,\n fileType?: string,\n policy: ContactAttachmentPolicy = DEFAULT_CONTACT_ATTACHMENT_POLICY,\n): ContactAttachmentMimeType | null {\n const normalizedType = fileType?.trim().toLowerCase()\n\n if (normalizedType && isAllowedContactAttachmentType(normalizedType, policy)) {\n return normalizedType\n }\n\n const extension = filename.toLowerCase().split('.').pop()\n if (!extension) return null\n\n const mappedType = EXTENSION_TO_MIME[extension]\n return mappedType && isAllowedContactAttachmentType(mappedType, policy) ? mappedType : null\n}\n\nexport function createContactAttachmentFilename(options: {\n index: number\n}) {\n return `image-${options.index + 1}.jpg`\n}\n\nexport function validateContactAttachment(\n file: { filename: string; size: number; type?: string },\n policy: ContactAttachmentPolicy = DEFAULT_CONTACT_ATTACHMENT_POLICY,\n):\n | { ok: true; contentType: ContactAttachmentMimeType }\n | { ok: false; message: string } {\n const contentType = resolveContactAttachmentType(file.filename, file.type, policy)\n\n if (!contentType) {\n return {\n ok: false,\n message: `\"${file.filename}\" is not a supported image type. Please upload ${contactAttachmentTypeLabel(\n policy,\n )} files.`,\n }\n }\n\n if (file.size > policy.maxFileSizeBytes) {\n const maximumMegabytes = Math.round((policy.maxFileSizeBytes / (1024 * 1024)) * 10) / 10\n return {\n ok: false,\n message: `Image \"${file.filename}\" is too large. Maximum size is ${maximumMegabytes} MB per image.`,\n }\n }\n\n return { ok: true, contentType }\n}\n\nexport type AdClickIds = {\n gclid?: string\n gbraid?: string\n wbraid?: string\n gadParams?: Record<string, string>\n}\n\nconst AD_CLICK_ID_FIELDS = [\n ['gclid', 'Google Click ID'],\n ['gbraid', 'GBRAID'],\n ['wbraid', 'WBRAID'],\n] as const\n\nexport function resolveTrackingSearchParams(queryParams: string, pageUrl = '') {\n const trimmedQueryParams = queryParams.trim()\n\n if (trimmedQueryParams) {\n return new URLSearchParams(\n trimmedQueryParams.startsWith('?') ? trimmedQueryParams : `?${trimmedQueryParams}`,\n )\n }\n\n if (pageUrl) {\n try {\n return new URL(pageUrl).searchParams\n } catch {\n return new URLSearchParams()\n }\n }\n\n return new URLSearchParams()\n}\n\nexport function resolveAdClickIds(queryParams: string, pageUrl = ''): AdClickIds {\n const params = resolveTrackingSearchParams(queryParams, pageUrl)\n const clickIds = AD_CLICK_ID_FIELDS.reduce<AdClickIds>((ids, [field]) => {\n const value = params.get(field)?.trim()\n\n if (value) {\n ids[field] = value\n }\n\n return ids\n }, {})\n const gadParams: Record<string, string> = {}\n\n params.forEach((value, key) => {\n const trimmedValue = value.trim()\n const normalizedKey = key.toLowerCase()\n\n if (trimmedValue && (normalizedKey === 'gad_source' || normalizedKey.startsWith('gad_'))) {\n gadParams[key] = trimmedValue\n }\n })\n\n return Object.keys(gadParams).length > 0 ? { ...clickIds, gadParams } : clickIds\n}\n\nexport function hasAdClickIds(adClickIds: AdClickIds) {\n return (\n AD_CLICK_ID_FIELDS.some(([field]) => Boolean(adClickIds[field])) ||\n Object.keys(adClickIds.gadParams ?? {}).length > 0\n )\n}\n\nexport function hasGoogleAdsClickId(queryParams: string, pageUrl = '') {\n return hasAdClickIds(resolveAdClickIds(queryParams, pageUrl))\n}\n\nfunction formatGadLabel(key: string) {\n return key\n .replace(/^gad_/, 'GAD ')\n .replace(/_/g, ' ')\n .replace(/campaignid\\b/i, 'campaign ID')\n .replace(/\\b\\w/g, (letter) => letter.toUpperCase())\n}\n\nexport function getAdClickIdRows(adClickIds: AdClickIds | undefined) {\n if (!adClickIds) return []\n\n const directRows = AD_CLICK_ID_FIELDS.flatMap(([field, label]) => {\n const value = adClickIds[field]\n return value ? [{ field, label, value }] : []\n })\n\n const gadRows = Object.entries(adClickIds.gadParams ?? {})\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([field, value]) => ({\n field,\n label: formatGadLabel(field),\n value,\n }))\n\n return [...directRows, ...gadRows]\n}\n\nexport function formatAdClickIdsForEmailText(adClickIds: AdClickIds) {\n return getAdClickIdRows(adClickIds)\n .map(({ label, value }) => `${label}: ${value}`)\n .join('\\n')\n}\n\nexport function parseContactEmailRecipients(value: string | undefined) {\n const recipients = value\n ?.split(',')\n .map((item) => item.trim())\n .filter(Boolean) ?? []\n const unique = recipients.filter((recipient, index) => {\n const address = recipient.match(/<([^<>]+)>$/)?.[1]?.trim() ?? recipient\n return recipients.findIndex((candidate) => {\n const candidateAddress = candidate.match(/<([^<>]+)>$/)?.[1]?.trim() ?? candidate\n return candidateAddress.toLowerCase() === address.toLowerCase()\n }) === index\n })\n if (unique.length > 5) {\n throw new Error('Contact delivery supports no more than five unique recipients.')\n }\n return unique\n}\n\nexport interface ContactFormEmailBaseData {\n name: string\n email: string\n phone: string\n message: string\n submittedAt: string\n submissionId: string\n pageLabel: string\n pagePath: string\n pageUrl: string\n trafficSource?: string\n initialLandingPath?: string\n initialLandingUrl?: string\n adClickIds?: AdClickIds\n attachmentCount?: number\n attachments?: readonly ContactFormEmailAttachmentSummary[]\n}\n\nexport type ContactFormEmailData = ContactFormEmailBaseData &\n ({ suburb: string; location?: string } | { location: string; suburb?: string })\n\nexport interface ContactFormEmailAttachmentSummary {\n filename: string\n contentType?: string\n sizeBytes?: number\n}\n\nexport interface ContactFormEmailRow {\n field: string\n label: string\n value: string\n}\n\nexport interface ContactFormEmailDataIssue {\n field: string\n message: string\n}\n\ntype ContactFormEmailValidationData = Partial<\n ContactFormEmailBaseData & {\n suburb: string\n location: string\n }\n>\n\nconst REQUIRED_CONTACT_FORM_EMAIL_LEAD_FIELDS = [\n 'name',\n 'email',\n 'phone',\n] as const\n\nconst REQUIRED_CONTACT_FORM_EMAIL_CONTEXT_FIELDS = [\n 'message',\n 'submittedAt',\n 'submissionId',\n 'pageLabel',\n 'pagePath',\n 'pageUrl',\n] as const\n\nfunction hasContactEmailValue(value: unknown) {\n return typeof value === 'string' ? value.trim().length > 0 : value != null\n}\n\nfunction contactEmailRow(field: string, label: string, value: unknown): ContactFormEmailRow[] {\n return hasContactEmailValue(value) ? [{ field, label, value: String(value) }] : []\n}\n\nexport function getContactFormEmailLocationValue(\n data: Pick<ContactFormEmailValidationData, 'location' | 'suburb'>,\n) {\n return data.suburb?.trim() || data.location?.trim() || ''\n}\n\nexport function validateContactFormEmailData(data: ContactFormEmailValidationData) {\n const issues: ContactFormEmailDataIssue[] = []\n\n REQUIRED_CONTACT_FORM_EMAIL_LEAD_FIELDS.forEach((field) => {\n if (!hasContactEmailValue(data[field])) {\n issues.push({ field, message: `Contact email data requires ${field}.` })\n }\n })\n\n if (!getContactFormEmailLocationValue(data)) {\n issues.push({\n field: 'suburbOrLocation',\n message: 'Contact email data requires a suburb or location.',\n })\n }\n\n REQUIRED_CONTACT_FORM_EMAIL_CONTEXT_FIELDS.forEach((field) => {\n if (!hasContactEmailValue(data[field])) {\n issues.push({ field, message: `Contact email data requires ${field}.` })\n }\n })\n\n return issues\n}\n\nexport function getContactFormEmailLeadRows(data: ContactFormEmailData) {\n const locationField = data.suburb ? 'suburb' : 'location'\n\n return [\n ...contactEmailRow('name', 'Name', data.name),\n ...contactEmailRow('email', 'Email', data.email),\n ...contactEmailRow('phone', 'Phone', data.phone),\n ...contactEmailRow(locationField, 'Location', getContactFormEmailLocationValue(data)),\n ...contactEmailRow('message', 'Message', data.message),\n ...contactEmailRow('submittedAt', 'Submitted at', data.submittedAt),\n ...contactEmailRow('submissionId', 'Submission ID', data.submissionId),\n ]\n}\n\nexport function formatContactFormEmailAttachmentSummary(\n data: Pick<ContactFormEmailBaseData, 'attachmentCount' | 'attachments'>,\n) {\n const attachments = data.attachments ?? []\n const attachmentCount = data.attachmentCount ?? attachments.length\n\n if (!attachmentCount) {\n return undefined\n }\n\n const countLabel = attachmentCount === 1 ? '1 file' : `${attachmentCount} files`\n // Original filenames may contain a person's name, address or device data.\n // Shared context rows retain only the operationally useful count.\n return countLabel\n}\n\nfunction privacySafeContextLabel(value: string | undefined) {\n const normalized = value?.trim()\n return normalized && normalized.length <= 160 && !/[\\u0000-\\u001f\\u007f]/.test(normalized)\n ? normalized\n : undefined\n}\n\nfunction privacySafeContextPath(value: string | undefined) {\n if (!value) return undefined\n try {\n return normalizeBrandlabRoutePath(value.split(/[?#]/, 1)[0] ?? '')\n } catch {\n return undefined\n }\n}\n\nfunction privacySafeContextUrl(value: string | undefined) {\n if (!value) return undefined\n try {\n const parsed = new URL(value)\n if (parsed.protocol !== 'https:' || parsed.username || parsed.password) return undefined\n const path = privacySafeContextPath(parsed.pathname)\n return path ? new URL(path, parsed.origin).toString() : undefined\n } catch {\n return undefined\n }\n}\n\nexport function getContactFormEmailContextRows(data: ContactFormEmailData) {\n const attachmentSummary = formatContactFormEmailAttachmentSummary(data)\n\n return [\n ...contactEmailRow('pageLabel', 'Page', privacySafeContextLabel(data.pageLabel)),\n ...contactEmailRow('pagePath', 'Page path', privacySafeContextPath(data.pagePath)),\n ...contactEmailRow('pageUrl', 'Page URL', privacySafeContextUrl(data.pageUrl)),\n ...contactEmailRow('attachments', 'Attachments', attachmentSummary),\n ]\n}\n\nconst AUSTRALIAN_PHONE_DIGITS = /^(?:0[23478]\\d{8}|61[23478]\\d{8})$/\n\nexport function normalizeAustralianPhoneDigits(phone: string) {\n return phone.replace(/[^\\d]/g, '')\n}\n\nexport function isValidAustralianPhoneNumber(phone: string) {\n return AUSTRALIAN_PHONE_DIGITS.test(normalizeAustralianPhoneDigits(phone))\n}\n\nexport const BRANDLAB_CONTACT_CONTRACT_VERSION = '4' as const\n\nexport const CONTACT_VISIT_SOURCES = [\n 'google-ads',\n 'google-business-profile',\n 'organic-search',\n 'referral',\n 'social',\n 'email-campaign',\n 'direct-or-unknown',\n] as const\n\nexport type ContactVisitSource = (typeof CONTACT_VISIT_SOURCES)[number]\n\nexport interface ContactAttributionSummary {\n initialLandingPath: string\n visitSource: ContactVisitSource\n visitSourceDetail?: string\n}\n\nexport function isContactVisitSource(value: unknown): value is ContactVisitSource {\n return CONTACT_VISIT_SOURCES.includes(value as ContactVisitSource)\n}\n\nexport function contactVisitSourceLabel(source: ContactVisitSource, detail?: string) {\n const labels: Record<ContactVisitSource, string> = {\n 'google-ads': 'Google Ads',\n 'google-business-profile': 'Google Business Profile',\n 'organic-search': 'Organic search',\n referral: 'Referral',\n social: 'Social',\n 'email-campaign': 'Email campaign',\n 'direct-or-unknown': 'Direct or unknown',\n }\n const label = labels[source]\n return detail && (source === 'referral' || source === 'social')\n ? `${label} — ${detail}`\n : label\n}\n\nexport type ContactFieldRole =\n | 'name'\n | 'email'\n | 'phone'\n | 'location'\n | 'message'\n | 'custom'\n\nexport type ContactFieldKind =\n | 'text'\n | 'email'\n | 'tel'\n | 'textarea'\n | 'select'\n | 'checkbox'\n\nexport type ContactFieldNormalization =\n | 'lowercase'\n | 'australian-phone-digits'\n\nexport type ContactPhoneRule = 'australian'\n\nexport interface ContactFieldContract {\n key: string\n role: ContactFieldRole\n kind: ContactFieldKind\n required: boolean\n /** Declarative limits remain shared instead of being recreated in projects. */\n maxLength?: number\n /** Required for select fields and prohibited for other field kinds. */\n options?: readonly string[]\n normalization?: ContactFieldNormalization\n /** Required for telephone fields so server validation has explicit semantics. */\n phoneRule?: ContactPhoneRule\n /** Required for checkbox fields; browsers submit this exact value when checked. */\n checkedValue?: string\n}\n\nexport interface BrandlabContactContract {\n projectId: string\n fieldProfile: string\n fields: readonly ContactFieldContract[]\n attachments: {\n mode: 'none' | 'images-optional' | 'images-required'\n }\n delivery: {\n recipientEnv: string\n /** Reviewed non-secret sender identity, for example Brand <noreply@example.com>. */\n sender: string\n }\n}\n\nexport type ContactActionResult =\n | {\n ok: true\n accepted: true\n trackConversion: true\n confirmationId: string\n }\n | {\n ok: true\n accepted: false\n trackConversion: false\n }\n | {\n ok: false\n code: 'validation' | 'attachment_rejected' | 'delivery_unavailable'\n retryable: boolean\n fieldErrors?: Record<string, string[]>\n }\n\nconst CONTACT_REQUIRED_ROLES = ['name', 'email', 'phone', 'message'] as const\nconst CONTACT_SINGLETON_ROLES = ['location'] as const\nconst ENV_KEY_PATTERN = /^[A-Z][A-Z0-9_]*$/\nconst CONTACT_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/\nconst CONTACT_FIELD_KEY_PATTERN = /^[a-z][A-Za-z0-9_-]{0,63}$/\nconst CONTACT_SENDER_PATTERN =\n /^(?:[^<>\\r\\n]{1,120}<)?[^\\s<>,@]+@[^\\s<>,@]+\\.[^\\s<>,@]+>?$/\nconst CONTACT_FIELD_ROLES = new Set<ContactFieldRole>([\n 'name', 'email', 'phone', 'location', 'message', 'custom',\n])\nconst CONTACT_FIELD_KINDS = new Set<ContactFieldKind>([\n 'text', 'email', 'tel', 'textarea', 'select', 'checkbox',\n])\nfunction assertContractText(value: string, field: string) {\n if (!value.trim()) throw new Error(`${field} cannot be empty.`)\n}\n\nfunction assertContactContractKeys(\n value: object,\n allowedKeys: readonly string[],\n field: string,\n) {\n const allowed = new Set(allowedKeys)\n const unsupported = Object.keys(value).filter((key) => !allowed.has(key))\n if (unsupported.length > 0) {\n throw new Error(`${field} contains unsupported keys: ${unsupported.join(', ')}.`)\n }\n}\n\nexport function defineContactContract<const T extends BrandlabContactContract>(contract: T): T {\n if (!contract || typeof contract !== 'object' || Array.isArray(contract)) {\n throw new Error('Contact contract must be an object.')\n }\n if (!Array.isArray(contract.fields)) {\n throw new Error('Contact contract fields must be an array.')\n }\n assertContactContractKeys(\n contract,\n ['projectId', 'fieldProfile', 'fields', 'attachments', 'delivery'],\n 'Contact contract',\n )\n if (\n !contract.attachments ||\n typeof contract.attachments !== 'object' ||\n !['none', 'images-optional', 'images-required'].includes(contract.attachments.mode)\n ) {\n throw new Error('Contact contract attachment mode is invalid.')\n }\n assertContactContractKeys(contract.attachments, ['mode'], 'Contact contract attachments')\n if (\n !contract.delivery ||\n typeof contract.delivery !== 'object' ||\n Array.isArray(contract.delivery)\n ) {\n throw new Error('Contact contract delivery environment is invalid.')\n }\n assertContactContractKeys(\n contract.delivery,\n ['recipientEnv', 'sender'],\n 'Contact contract delivery',\n )\n assertContractText(contract.projectId, 'projectId')\n assertContractText(contract.fieldProfile, 'fieldProfile')\n assertBrandlabCanonicalEntityId(contract.projectId)\n if (!CONTACT_ID_PATTERN.test(contract.fieldProfile)) {\n throw new Error('fieldProfile must be lowercase kebab-case.')\n }\n if (contract.fields.length < CONTACT_REQUIRED_ROLES.length || contract.fields.length > 20) {\n throw new Error('Contact contract must define between 4 and 20 fields.')\n }\n\n const duplicateKey = contract.fields.find((field, index) =>\n contract.fields.slice(0, index).some((candidate) => candidate.key === field.key),\n )\n if (duplicateKey) throw new Error(`Contact contract contains duplicate field key \"${duplicateKey.key}\".`)\n\n contract.fields.forEach((field, index) => {\n assertContactContractKeys(\n field,\n [\n 'key',\n 'role',\n 'kind',\n 'required',\n 'maxLength',\n 'options',\n 'normalization',\n 'phoneRule',\n 'checkedValue',\n ],\n `fields[${index}]`,\n )\n assertContractText(field.key, `fields[${index}].key`)\n if (!CONTACT_FIELD_KEY_PATTERN.test(field.key)) {\n throw new Error(`fields[${index}].key is invalid.`)\n }\n if (!CONTACT_FIELD_ROLES.has(field.role)) {\n throw new Error(`fields[${index}].role is invalid.`)\n }\n if (!CONTACT_FIELD_KINDS.has(field.kind)) {\n throw new Error(`fields[${index}].kind is invalid.`)\n }\n if (typeof field.required !== 'boolean') {\n throw new Error(`fields[${index}].required must be boolean.`)\n }\n if (\n field.maxLength !== undefined &&\n (!Number.isSafeInteger(field.maxLength) || field.maxLength < 1 || field.maxLength > 10_000)\n ) {\n throw new Error(`fields[${index}].maxLength is invalid.`)\n }\n if (field.kind === 'select') {\n if (\n !Array.isArray(field.options) ||\n field.options.length === 0 ||\n field.options.length > 100 ||\n field.options.some(\n (option: unknown) =>\n typeof option !== 'string' ||\n option.trim() !== option ||\n !option ||\n option.length > 200,\n ) ||\n new Set(field.options).size !== field.options.length\n ) {\n throw new Error(`fields[${index}].options must be a bounded unique select allowlist.`)\n }\n } else if (field.options !== undefined) {\n throw new Error(`fields[${index}].options is supported only for select fields.`)\n }\n if (\n field.normalization !== undefined &&\n !['lowercase', 'australian-phone-digits'].includes(field.normalization)\n ) {\n throw new Error(`fields[${index}].normalization is unsupported.`)\n }\n if (\n field.normalization === 'australian-phone-digits' &&\n field.kind !== 'tel'\n ) {\n throw new Error(`fields[${index}] uses phone normalization without a tel kind.`)\n }\n if (field.kind === 'tel') {\n if (field.phoneRule !== 'australian') {\n throw new Error(`fields[${index}].phoneRule must declare Australian phone semantics.`)\n }\n if (field.normalization !== 'australian-phone-digits') {\n throw new Error(`fields[${index}].normalization must use Australian phone digits.`)\n }\n } else if (field.phoneRule !== undefined) {\n throw new Error(`fields[${index}].phoneRule is supported only for tel fields.`)\n }\n if (field.kind === 'email' && field.normalization !== 'lowercase') {\n throw new Error(`fields[${index}].normalization must lowercase email values.`)\n }\n if (field.kind === 'checkbox') {\n if (\n typeof field.checkedValue !== 'string' ||\n !field.checkedValue ||\n field.checkedValue.trim() !== field.checkedValue ||\n field.checkedValue.length > 100\n ) {\n throw new Error(`fields[${index}].checkedValue is required for checkbox fields.`)\n }\n } else if (field.checkedValue !== undefined) {\n throw new Error(`fields[${index}].checkedValue is supported only for checkbox fields.`)\n }\n })\n\n CONTACT_REQUIRED_ROLES.forEach((role) => {\n const matches = contract.fields.filter((field) => field.role === role)\n if (matches.length !== 1 || !matches[0]?.required) {\n throw new Error(`Contact contract requires exactly one required \"${role}\" field.`)\n }\n })\n CONTACT_SINGLETON_ROLES.forEach((role) => {\n if (contract.fields.filter((field) => field.role === role).length > 1) {\n throw new Error(`Contact contract allows at most one \"${role}\" field.`)\n }\n })\n\n const roleKinds: Partial<Record<ContactFieldRole, readonly ContactFieldKind[]>> = {\n name: ['text'],\n email: ['email'],\n phone: ['tel'],\n location: ['text', 'select'],\n message: ['textarea'],\n }\n contract.fields.forEach((field) => {\n const role = field.role as ContactFieldRole\n const kind = field.kind as ContactFieldKind\n const allowedKinds = roleKinds[role]\n if (allowedKinds && !allowedKinds.includes(kind)) {\n throw new Error(`Contact field \"${field.key}\" has an invalid kind for role \"${field.role}\".`)\n }\n })\n\n if (!ENV_KEY_PATTERN.test(contract.delivery.recipientEnv)) {\n throw new Error('delivery.recipientEnv must be an environment variable key.')\n }\n if (\n !CONTACT_SENDER_PATTERN.test(contract.delivery.sender) ||\n contract.delivery.sender.length > 320 ||\n /[\\u0000-\\u001f\\u007f]/.test(contract.delivery.sender)\n ) {\n throw new Error('delivery.sender must be a reviewed email sender.')\n }\n\n return contract\n}\n"]}
|
|
1
|
+
{"version":3,"file":"contact.js","sourceRoot":"","sources":["../src/contact.ts"],"names":[],"mappings":"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","sourcesContent":["import { normalizeBrandlabRoutePath } from './routes.js'\nimport { assertBrandlabCanonicalEntityId } from './entity-id.js'\n\nexport interface ContactAttachmentPolicy {\n maxFiles: number\n maxFileSizeBytes: number\n maxTotalSizeBytes: number\n allowedTypes: readonly ContactAttachmentMimeType[]\n}\n\nexport type ContactAttachmentMimeType =\n | 'application/pdf'\n | 'image/jpeg'\n | 'image/jpg'\n | 'image/png'\n | 'image/webp'\n | 'image/gif'\n | 'image/heic'\n | 'image/heif'\n\nexport const DEFAULT_CONTACT_ATTACHMENT_POLICY = {\n maxFiles: 5,\n maxFileSizeBytes: 4_000_000,\n maxTotalSizeBytes: 4_000_000,\n allowedTypes: [\n 'image/jpeg',\n 'image/jpg',\n 'image/png',\n 'image/webp',\n 'image/gif',\n 'image/heic',\n 'image/heif',\n ],\n} as const satisfies ContactAttachmentPolicy\n\nexport const DOCUMENT_CONTACT_ATTACHMENT_POLICY = {\n ...DEFAULT_CONTACT_ATTACHMENT_POLICY,\n allowedTypes: [...DEFAULT_CONTACT_ATTACHMENT_POLICY.allowedTypes, 'application/pdf'],\n} as const satisfies ContactAttachmentPolicy\n\nconst EXTENSION_TO_MIME: Record<string, ContactAttachmentMimeType> = {\n pdf: 'application/pdf',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n gif: 'image/gif',\n webp: 'image/webp',\n heic: 'image/heic',\n heif: 'image/heif',\n}\n\nexport function contactAttachmentAccept(\n policy: ContactAttachmentPolicy = DEFAULT_CONTACT_ATTACHMENT_POLICY,\n) {\n return [...new Set(policy.allowedTypes)].join(',')\n}\n\nexport function contactAttachmentTypeLabel(\n policy: ContactAttachmentPolicy = DEFAULT_CONTACT_ATTACHMENT_POLICY,\n) {\n const labels = policy.allowedTypes\n .map((type) => {\n const subtype = type.split('/')[1] ?? type\n if (subtype === 'jpeg' || subtype === 'jpg') return 'JPG'\n if (subtype === 'webp') return 'WebP'\n return subtype.toUpperCase()\n })\n .filter((label, index, labels) => labels.indexOf(label) === index)\n\n if (labels.length <= 1) return labels[0] ?? ''\n return `${labels.slice(0, -1).join(', ')}, or ${labels[labels.length - 1]}`\n}\n\nexport function isAllowedContactAttachmentType(\n value: string,\n policy: ContactAttachmentPolicy = DEFAULT_CONTACT_ATTACHMENT_POLICY,\n): value is ContactAttachmentMimeType {\n return policy.allowedTypes.includes(value as ContactAttachmentMimeType)\n}\n\nexport function resolveContactAttachmentType(\n filename: string,\n fileType?: string,\n policy: ContactAttachmentPolicy = DEFAULT_CONTACT_ATTACHMENT_POLICY,\n): ContactAttachmentMimeType | null {\n const normalizedType = fileType?.trim().toLowerCase()\n\n if (normalizedType && isAllowedContactAttachmentType(normalizedType, policy)) {\n return normalizedType\n }\n\n const extension = filename.toLowerCase().split('.').pop()\n if (!extension) return null\n\n const mappedType = EXTENSION_TO_MIME[extension]\n return mappedType && isAllowedContactAttachmentType(mappedType, policy) ? mappedType : null\n}\n\nexport function createContactAttachmentFilename(options: {\n index: number\n contentType?: string\n}) {\n if (options.contentType === 'application/pdf') return `document-${options.index + 1}.pdf`\n return `image-${options.index + 1}.jpg`\n}\n\nexport function validateContactAttachment(\n file: { filename: string; size: number; type?: string },\n policy: ContactAttachmentPolicy = DEFAULT_CONTACT_ATTACHMENT_POLICY,\n):\n | { ok: true; contentType: ContactAttachmentMimeType }\n | { ok: false; message: string } {\n const contentType = resolveContactAttachmentType(file.filename, file.type, policy)\n\n if (!contentType) {\n return {\n ok: false,\n message: `\"${file.filename}\" is not a supported image type. Please upload ${contactAttachmentTypeLabel(\n policy,\n )} files.`,\n }\n }\n\n if (file.size > policy.maxFileSizeBytes) {\n const maximumMegabytes = Math.round((policy.maxFileSizeBytes / (1024 * 1024)) * 10) / 10\n return {\n ok: false,\n message: `Image \"${file.filename}\" is too large. Maximum size is ${maximumMegabytes} MB per image.`,\n }\n }\n\n return { ok: true, contentType }\n}\n\nexport type AdClickIds = {\n gclid?: string\n gbraid?: string\n wbraid?: string\n gadParams?: Record<string, string>\n}\n\nconst AD_CLICK_ID_FIELDS = [\n ['gclid', 'Google Click ID'],\n ['gbraid', 'GBRAID'],\n ['wbraid', 'WBRAID'],\n] as const\n\nexport function resolveTrackingSearchParams(queryParams: string, pageUrl = '') {\n const trimmedQueryParams = queryParams.trim()\n\n if (trimmedQueryParams) {\n return new URLSearchParams(\n trimmedQueryParams.startsWith('?') ? trimmedQueryParams : `?${trimmedQueryParams}`,\n )\n }\n\n if (pageUrl) {\n try {\n return new URL(pageUrl).searchParams\n } catch {\n return new URLSearchParams()\n }\n }\n\n return new URLSearchParams()\n}\n\nexport function resolveAdClickIds(queryParams: string, pageUrl = ''): AdClickIds {\n const params = resolveTrackingSearchParams(queryParams, pageUrl)\n const clickIds = AD_CLICK_ID_FIELDS.reduce<AdClickIds>((ids, [field]) => {\n const value = params.get(field)?.trim()\n\n if (value) {\n ids[field] = value\n }\n\n return ids\n }, {})\n const gadParams: Record<string, string> = {}\n\n params.forEach((value, key) => {\n const trimmedValue = value.trim()\n const normalizedKey = key.toLowerCase()\n\n if (trimmedValue && (normalizedKey === 'gad_source' || normalizedKey.startsWith('gad_'))) {\n gadParams[key] = trimmedValue\n }\n })\n\n return Object.keys(gadParams).length > 0 ? { ...clickIds, gadParams } : clickIds\n}\n\nexport function hasAdClickIds(adClickIds: AdClickIds) {\n return (\n AD_CLICK_ID_FIELDS.some(([field]) => Boolean(adClickIds[field])) ||\n Object.keys(adClickIds.gadParams ?? {}).length > 0\n )\n}\n\nexport function hasGoogleAdsClickId(queryParams: string, pageUrl = '') {\n return hasAdClickIds(resolveAdClickIds(queryParams, pageUrl))\n}\n\nfunction formatGadLabel(key: string) {\n return key\n .replace(/^gad_/, 'GAD ')\n .replace(/_/g, ' ')\n .replace(/campaignid\\b/i, 'campaign ID')\n .replace(/\\b\\w/g, (letter) => letter.toUpperCase())\n}\n\nexport function getAdClickIdRows(adClickIds: AdClickIds | undefined) {\n if (!adClickIds) return []\n\n const directRows = AD_CLICK_ID_FIELDS.flatMap(([field, label]) => {\n const value = adClickIds[field]\n return value ? [{ field, label, value }] : []\n })\n\n const gadRows = Object.entries(adClickIds.gadParams ?? {})\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([field, value]) => ({\n field,\n label: formatGadLabel(field),\n value,\n }))\n\n return [...directRows, ...gadRows]\n}\n\nexport function formatAdClickIdsForEmailText(adClickIds: AdClickIds) {\n return getAdClickIdRows(adClickIds)\n .map(({ label, value }) => `${label}: ${value}`)\n .join('\\n')\n}\n\nexport function parseContactEmailRecipients(value: string | undefined) {\n const recipients = value\n ?.split(',')\n .map((item) => item.trim())\n .filter(Boolean) ?? []\n const unique = recipients.filter((recipient, index) => {\n const address = recipient.match(/<([^<>]+)>$/)?.[1]?.trim() ?? recipient\n return recipients.findIndex((candidate) => {\n const candidateAddress = candidate.match(/<([^<>]+)>$/)?.[1]?.trim() ?? candidate\n return candidateAddress.toLowerCase() === address.toLowerCase()\n }) === index\n })\n if (unique.length > 5) {\n throw new Error('Contact delivery supports no more than five unique recipients.')\n }\n return unique\n}\n\nexport interface ContactFormEmailBaseData {\n name: string\n email: string\n phone: string\n message: string\n submittedAt: string\n submissionId: string\n pageLabel: string\n pagePath: string\n pageUrl: string\n trafficSource?: string\n initialLandingPath?: string\n initialLandingUrl?: string\n adClickIds?: AdClickIds\n attachmentCount?: number\n attachments?: readonly ContactFormEmailAttachmentSummary[]\n}\n\nexport type ContactFormEmailData = ContactFormEmailBaseData &\n ({ suburb: string; location?: string } | { location: string; suburb?: string })\n\nexport interface ContactFormEmailAttachmentSummary {\n filename: string\n contentType?: string\n sizeBytes?: number\n}\n\nexport interface ContactFormEmailRow {\n field: string\n label: string\n value: string\n}\n\nexport interface ContactFormEmailDataIssue {\n field: string\n message: string\n}\n\ntype ContactFormEmailValidationData = Partial<\n ContactFormEmailBaseData & {\n suburb: string\n location: string\n }\n>\n\nconst REQUIRED_CONTACT_FORM_EMAIL_LEAD_FIELDS = [\n 'name',\n 'email',\n 'phone',\n] as const\n\nconst REQUIRED_CONTACT_FORM_EMAIL_CONTEXT_FIELDS = [\n 'message',\n 'submittedAt',\n 'submissionId',\n 'pageLabel',\n 'pagePath',\n 'pageUrl',\n] as const\n\nfunction hasContactEmailValue(value: unknown) {\n return typeof value === 'string' ? value.trim().length > 0 : value != null\n}\n\nfunction contactEmailRow(field: string, label: string, value: unknown): ContactFormEmailRow[] {\n return hasContactEmailValue(value) ? [{ field, label, value: String(value) }] : []\n}\n\nexport function getContactFormEmailLocationValue(\n data: Pick<ContactFormEmailValidationData, 'location' | 'suburb'>,\n) {\n return data.suburb?.trim() || data.location?.trim() || ''\n}\n\nexport function validateContactFormEmailData(data: ContactFormEmailValidationData) {\n const issues: ContactFormEmailDataIssue[] = []\n\n REQUIRED_CONTACT_FORM_EMAIL_LEAD_FIELDS.forEach((field) => {\n if (!hasContactEmailValue(data[field])) {\n issues.push({ field, message: `Contact email data requires ${field}.` })\n }\n })\n\n if (!getContactFormEmailLocationValue(data)) {\n issues.push({\n field: 'suburbOrLocation',\n message: 'Contact email data requires a suburb or location.',\n })\n }\n\n REQUIRED_CONTACT_FORM_EMAIL_CONTEXT_FIELDS.forEach((field) => {\n if (!hasContactEmailValue(data[field])) {\n issues.push({ field, message: `Contact email data requires ${field}.` })\n }\n })\n\n return issues\n}\n\nexport function getContactFormEmailLeadRows(data: ContactFormEmailData) {\n const locationField = data.suburb ? 'suburb' : 'location'\n\n return [\n ...contactEmailRow('name', 'Name', data.name),\n ...contactEmailRow('email', 'Email', data.email),\n ...contactEmailRow('phone', 'Phone', data.phone),\n ...contactEmailRow(locationField, 'Location', getContactFormEmailLocationValue(data)),\n ...contactEmailRow('message', 'Message', data.message),\n ...contactEmailRow('submittedAt', 'Submitted at', data.submittedAt),\n ...contactEmailRow('submissionId', 'Submission ID', data.submissionId),\n ]\n}\n\nexport function formatContactFormEmailAttachmentSummary(\n data: Pick<ContactFormEmailBaseData, 'attachmentCount' | 'attachments'>,\n) {\n const attachments = data.attachments ?? []\n const attachmentCount = data.attachmentCount ?? attachments.length\n\n if (!attachmentCount) {\n return undefined\n }\n\n const countLabel = attachmentCount === 1 ? '1 file' : `${attachmentCount} files`\n // Original filenames may contain a person's name, address or device data.\n // Shared context rows retain only the operationally useful count.\n return countLabel\n}\n\nfunction privacySafeContextLabel(value: string | undefined) {\n const normalized = value?.trim()\n return normalized && normalized.length <= 160 && !/[\\u0000-\\u001f\\u007f]/.test(normalized)\n ? normalized\n : undefined\n}\n\nfunction privacySafeContextPath(value: string | undefined) {\n if (!value) return undefined\n try {\n return normalizeBrandlabRoutePath(value.split(/[?#]/, 1)[0] ?? '')\n } catch {\n return undefined\n }\n}\n\nfunction privacySafeContextUrl(value: string | undefined) {\n if (!value) return undefined\n try {\n const parsed = new URL(value)\n if (parsed.protocol !== 'https:' || parsed.username || parsed.password) return undefined\n const path = privacySafeContextPath(parsed.pathname)\n return path ? new URL(path, parsed.origin).toString() : undefined\n } catch {\n return undefined\n }\n}\n\nexport function getContactFormEmailContextRows(data: ContactFormEmailData) {\n const attachmentSummary = formatContactFormEmailAttachmentSummary(data)\n\n return [\n ...contactEmailRow('pageLabel', 'Page', privacySafeContextLabel(data.pageLabel)),\n ...contactEmailRow('pagePath', 'Page path', privacySafeContextPath(data.pagePath)),\n ...contactEmailRow('pageUrl', 'Page URL', privacySafeContextUrl(data.pageUrl)),\n ...contactEmailRow('attachments', 'Attachments', attachmentSummary),\n ]\n}\n\nconst AUSTRALIAN_PHONE_DIGITS = /^(?:0[23478]\\d{8}|61[23478]\\d{8})$/\n\nexport function normalizeAustralianPhoneDigits(phone: string) {\n return phone.replace(/[^\\d]/g, '')\n}\n\nexport function isValidAustralianPhoneNumber(phone: string) {\n return AUSTRALIAN_PHONE_DIGITS.test(normalizeAustralianPhoneDigits(phone))\n}\n\nexport const BRANDLAB_CONTACT_CONTRACT_VERSION = '4' as const\n\nexport const CONTACT_VISIT_SOURCES = [\n 'google-ads',\n 'google-business-profile',\n 'organic-search',\n 'referral',\n 'social',\n 'email-campaign',\n 'direct-or-unknown',\n] as const\n\nexport type ContactVisitSource = (typeof CONTACT_VISIT_SOURCES)[number]\n\nexport interface ContactAttributionSummary {\n initialLandingPath: string\n visitSource: ContactVisitSource\n visitSourceDetail?: string\n}\n\nexport function isContactVisitSource(value: unknown): value is ContactVisitSource {\n return CONTACT_VISIT_SOURCES.includes(value as ContactVisitSource)\n}\n\nexport function contactVisitSourceLabel(source: ContactVisitSource, detail?: string) {\n const labels: Record<ContactVisitSource, string> = {\n 'google-ads': 'Google Ads',\n 'google-business-profile': 'Google Business Profile',\n 'organic-search': 'Organic search',\n referral: 'Referral',\n social: 'Social',\n 'email-campaign': 'Email campaign',\n 'direct-or-unknown': 'Direct or unknown',\n }\n const label = labels[source]\n return detail && (source === 'referral' || source === 'social')\n ? `${label} — ${detail}`\n : label\n}\n\nexport type ContactFieldRole =\n | 'name'\n | 'email'\n | 'phone'\n | 'location'\n | 'message'\n | 'custom'\n\nexport type ContactFieldKind =\n | 'text'\n | 'email'\n | 'tel'\n | 'textarea'\n | 'select'\n | 'checkbox'\n\nexport type ContactFieldNormalization =\n | 'lowercase'\n | 'australian-phone-digits'\n\nexport type ContactPhoneRule = 'australian'\n\nexport interface ContactFieldContract {\n key: string\n role: ContactFieldRole\n kind: ContactFieldKind\n required: boolean\n /** Declarative limits remain shared instead of being recreated in projects. */\n maxLength?: number\n /** Required for select fields and prohibited for other field kinds. */\n options?: readonly string[]\n normalization?: ContactFieldNormalization\n /** Required for telephone fields so server validation has explicit semantics. */\n phoneRule?: ContactPhoneRule\n /** Required for checkbox fields; browsers submit this exact value when checked. */\n checkedValue?: string\n}\n\nexport interface BrandlabContactContract {\n projectId: string\n fieldProfile: string\n fields: readonly ContactFieldContract[]\n attachments: {\n mode: 'none' | 'images-optional' | 'images-required' | 'images-and-pdfs-optional'\n }\n delivery: {\n recipientEnv: string\n /** Reviewed non-secret sender identity, for example Brand <noreply@example.com>. */\n sender: string\n }\n}\n\nexport type ContactActionResult =\n | {\n ok: true\n accepted: true\n trackConversion: true\n confirmationId: string\n }\n | {\n ok: true\n accepted: false\n trackConversion: false\n }\n | {\n ok: false\n code: 'validation' | 'attachment_rejected' | 'delivery_unavailable'\n retryable: boolean\n fieldErrors?: Record<string, string[]>\n }\n\nconst CONTACT_REQUIRED_ROLES = ['name', 'email', 'phone', 'message'] as const\nconst CONTACT_SINGLETON_ROLES = ['location'] as const\nconst ENV_KEY_PATTERN = /^[A-Z][A-Z0-9_]*$/\nconst CONTACT_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/\nconst CONTACT_FIELD_KEY_PATTERN = /^[a-z][A-Za-z0-9_-]{0,63}$/\nconst CONTACT_SENDER_PATTERN =\n /^(?:[^<>\\r\\n]{1,120}<)?[^\\s<>,@]+@[^\\s<>,@]+\\.[^\\s<>,@]+>?$/\nconst CONTACT_FIELD_ROLES = new Set<ContactFieldRole>([\n 'name', 'email', 'phone', 'location', 'message', 'custom',\n])\nconst CONTACT_FIELD_KINDS = new Set<ContactFieldKind>([\n 'text', 'email', 'tel', 'textarea', 'select', 'checkbox',\n])\nfunction assertContractText(value: string, field: string) {\n if (!value.trim()) throw new Error(`${field} cannot be empty.`)\n}\n\nfunction assertContactContractKeys(\n value: object,\n allowedKeys: readonly string[],\n field: string,\n) {\n const allowed = new Set(allowedKeys)\n const unsupported = Object.keys(value).filter((key) => !allowed.has(key))\n if (unsupported.length > 0) {\n throw new Error(`${field} contains unsupported keys: ${unsupported.join(', ')}.`)\n }\n}\n\nexport function defineContactContract<const T extends BrandlabContactContract>(contract: T): T {\n if (!contract || typeof contract !== 'object' || Array.isArray(contract)) {\n throw new Error('Contact contract must be an object.')\n }\n if (!Array.isArray(contract.fields)) {\n throw new Error('Contact contract fields must be an array.')\n }\n assertContactContractKeys(\n contract,\n ['projectId', 'fieldProfile', 'fields', 'attachments', 'delivery'],\n 'Contact contract',\n )\n if (\n !contract.attachments ||\n typeof contract.attachments !== 'object' ||\n !['none', 'images-optional', 'images-required', 'images-and-pdfs-optional'].includes(contract.attachments.mode)\n ) {\n throw new Error('Contact contract attachment mode is invalid.')\n }\n assertContactContractKeys(contract.attachments, ['mode'], 'Contact contract attachments')\n if (\n !contract.delivery ||\n typeof contract.delivery !== 'object' ||\n Array.isArray(contract.delivery)\n ) {\n throw new Error('Contact contract delivery environment is invalid.')\n }\n assertContactContractKeys(\n contract.delivery,\n ['recipientEnv', 'sender'],\n 'Contact contract delivery',\n )\n assertContractText(contract.projectId, 'projectId')\n assertContractText(contract.fieldProfile, 'fieldProfile')\n assertBrandlabCanonicalEntityId(contract.projectId)\n if (!CONTACT_ID_PATTERN.test(contract.fieldProfile)) {\n throw new Error('fieldProfile must be lowercase kebab-case.')\n }\n if (contract.fields.length < CONTACT_REQUIRED_ROLES.length || contract.fields.length > 20) {\n throw new Error('Contact contract must define between 4 and 20 fields.')\n }\n\n const duplicateKey = contract.fields.find((field, index) =>\n contract.fields.slice(0, index).some((candidate) => candidate.key === field.key),\n )\n if (duplicateKey) throw new Error(`Contact contract contains duplicate field key \"${duplicateKey.key}\".`)\n\n contract.fields.forEach((field, index) => {\n assertContactContractKeys(\n field,\n [\n 'key',\n 'role',\n 'kind',\n 'required',\n 'maxLength',\n 'options',\n 'normalization',\n 'phoneRule',\n 'checkedValue',\n ],\n `fields[${index}]`,\n )\n assertContractText(field.key, `fields[${index}].key`)\n if (!CONTACT_FIELD_KEY_PATTERN.test(field.key)) {\n throw new Error(`fields[${index}].key is invalid.`)\n }\n if (!CONTACT_FIELD_ROLES.has(field.role)) {\n throw new Error(`fields[${index}].role is invalid.`)\n }\n if (!CONTACT_FIELD_KINDS.has(field.kind)) {\n throw new Error(`fields[${index}].kind is invalid.`)\n }\n if (typeof field.required !== 'boolean') {\n throw new Error(`fields[${index}].required must be boolean.`)\n }\n if (\n field.maxLength !== undefined &&\n (!Number.isSafeInteger(field.maxLength) || field.maxLength < 1 || field.maxLength > 10_000)\n ) {\n throw new Error(`fields[${index}].maxLength is invalid.`)\n }\n if (field.kind === 'select') {\n if (\n !Array.isArray(field.options) ||\n field.options.length === 0 ||\n field.options.length > 100 ||\n field.options.some(\n (option: unknown) =>\n typeof option !== 'string' ||\n option.trim() !== option ||\n !option ||\n option.length > 200,\n ) ||\n new Set(field.options).size !== field.options.length\n ) {\n throw new Error(`fields[${index}].options must be a bounded unique select allowlist.`)\n }\n } else if (field.options !== undefined) {\n throw new Error(`fields[${index}].options is supported only for select fields.`)\n }\n if (\n field.normalization !== undefined &&\n !['lowercase', 'australian-phone-digits'].includes(field.normalization)\n ) {\n throw new Error(`fields[${index}].normalization is unsupported.`)\n }\n if (\n field.normalization === 'australian-phone-digits' &&\n field.kind !== 'tel'\n ) {\n throw new Error(`fields[${index}] uses phone normalization without a tel kind.`)\n }\n if (field.kind === 'tel') {\n if (field.phoneRule !== 'australian') {\n throw new Error(`fields[${index}].phoneRule must declare Australian phone semantics.`)\n }\n if (field.normalization !== 'australian-phone-digits') {\n throw new Error(`fields[${index}].normalization must use Australian phone digits.`)\n }\n } else if (field.phoneRule !== undefined) {\n throw new Error(`fields[${index}].phoneRule is supported only for tel fields.`)\n }\n if (field.kind === 'email' && field.normalization !== 'lowercase') {\n throw new Error(`fields[${index}].normalization must lowercase email values.`)\n }\n if (field.kind === 'checkbox') {\n if (\n typeof field.checkedValue !== 'string' ||\n !field.checkedValue ||\n field.checkedValue.trim() !== field.checkedValue ||\n field.checkedValue.length > 100\n ) {\n throw new Error(`fields[${index}].checkedValue is required for checkbox fields.`)\n }\n } else if (field.checkedValue !== undefined) {\n throw new Error(`fields[${index}].checkedValue is supported only for checkbox fields.`)\n }\n })\n\n CONTACT_REQUIRED_ROLES.forEach((role) => {\n const matches = contract.fields.filter((field) => field.role === role)\n if (matches.length !== 1 || !matches[0]?.required) {\n throw new Error(`Contact contract requires exactly one required \"${role}\" field.`)\n }\n })\n CONTACT_SINGLETON_ROLES.forEach((role) => {\n if (contract.fields.filter((field) => field.role === role).length > 1) {\n throw new Error(`Contact contract allows at most one \"${role}\" field.`)\n }\n })\n\n const roleKinds: Partial<Record<ContactFieldRole, readonly ContactFieldKind[]>> = {\n name: ['text'],\n email: ['email'],\n phone: ['tel'],\n location: ['text', 'select'],\n message: ['textarea'],\n }\n contract.fields.forEach((field) => {\n const role = field.role as ContactFieldRole\n const kind = field.kind as ContactFieldKind\n const allowedKinds = roleKinds[role]\n if (allowedKinds && !allowedKinds.includes(kind)) {\n throw new Error(`Contact field \"${field.key}\" has an invalid kind for role \"${field.role}\".`)\n }\n })\n\n if (!ENV_KEY_PATTERN.test(contract.delivery.recipientEnv)) {\n throw new Error('delivery.recipientEnv must be an environment variable key.')\n }\n if (\n !CONTACT_SENDER_PATTERN.test(contract.delivery.sender) ||\n contract.delivery.sender.length > 320 ||\n /[\\u0000-\\u001f\\u007f]/.test(contract.delivery.sender)\n ) {\n throw new Error('delivery.sender must be a reviewed email sender.')\n }\n\n return contract\n}\n"]}
|
package/dist/project.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { GooglePlaceReviewsSelection } from './google-reviews.js';
|
|
|
2
2
|
export type BrandlabProjectProfile = 'next-local-service' | 'next-root-level';
|
|
3
3
|
export type BrandlabRouteFamily = 'services' | 'locations' | 'service-locations' | 'blog' | 'projects' | 'legal';
|
|
4
4
|
export type BrandlabBlogAdapter = 'none' | 'mdx' | 'structured';
|
|
5
|
-
export type BrandlabContactAttachmentMode = 'none' | 'images-optional' | 'images-required';
|
|
5
|
+
export type BrandlabContactAttachmentMode = 'none' | 'images-optional' | 'images-required' | 'images-and-pdfs-optional';
|
|
6
6
|
export type BrandlabLocationStrategy = 'none' | 'approved-service-area' | 'physical-location' | 'multi-location';
|
|
7
7
|
export type BrandlabReviewsProvider = 'google-places' | 'project-owned';
|
|
8
8
|
export type BrandlabGoogleReviewsMode = 'link-only' | 'summary' | 'full';
|
package/dist/project.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAA;AAEtE,MAAM,MAAM,sBAAsB,GAC9B,oBAAoB,GACpB,iBAAiB,CAAA;AAErB,MAAM,MAAM,mBAAmB,GAC3B,UAAU,GACV,WAAW,GACX,mBAAmB,GACnB,MAAM,GACN,UAAU,GACV,OAAO,CAAA;AAEX,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,KAAK,GAAG,YAAY,CAAA;AAC/D,MAAM,MAAM,6BAA6B,GACrC,MAAM,GACN,iBAAiB,GACjB,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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"}
|
package/dist/project.js
CHANGED
package/dist/project.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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","sourcesContent":["import { normalizeBrandlabRoutePath } from './routes.js'\nimport { assertBrandlabCanonicalEntityId } from './entity-id.js'\nimport type { GooglePlaceReviewsSelection } from './google-reviews.js'\n\nexport type BrandlabProjectProfile =\n | 'next-local-service'\n | 'next-root-level'\n\nexport type BrandlabRouteFamily =\n | 'services'\n | 'locations'\n | 'service-locations'\n | 'blog'\n | 'projects'\n | 'legal'\n\nexport type BrandlabBlogAdapter = 'none' | 'mdx' | 'structured'\nexport type BrandlabContactAttachmentMode =\n | 'none'\n | 'images-optional'\n | 'images-required'\nexport type BrandlabLocationStrategy =\n | 'none'\n | 'approved-service-area'\n | 'physical-location'\n | 'multi-location'\nexport type BrandlabReviewsProvider = 'google-places' | 'project-owned'\nexport type BrandlabGoogleReviewsMode = 'link-only' | 'summary' | 'full'\n\nexport type BrandlabReviewsCapability =\n | { provider: 'project-owned' }\n | {\n provider: 'google-places'\n mode: BrandlabGoogleReviewsMode\n /** Optional presentation policy. Five-star selection is valid only in full mode. */\n selection?: GooglePlaceReviewsSelection\n /** Stable route IDs where review proof is intentionally presented. */\n routeIds: readonly string[]\n }\n\nexport interface BrandlabRouteAlias {\n from: string\n to: string\n status?: 301 | 308\n}\n\nexport interface BrandlabException {\n ruleId: string\n rationale: string\n owner: 'Romie'\n evidence: string\n expiresAt?: string\n reviewTrigger: string\n}\n\nexport type BrandlabSitemapConfig =\n | { mode: 'single' }\n | {\n mode: 'split'\n families: readonly string[]\n reason: string\n }\n\nexport interface BrandlabProjectConfig {\n projectId: string\n profile: BrandlabProjectProfile\n routes: {\n families: readonly BrandlabRouteFamily[]\n aliases?: readonly BrandlabRouteAlias[]\n }\n capabilities?: {\n blog?: { adapter: BrandlabBlogAdapter }\n contact?: {\n fieldProfile: string\n attachments: BrandlabContactAttachmentMode\n }\n locations?: { strategy: BrandlabLocationStrategy }\n reviews?: BrandlabReviewsCapability\n }\n sitemap?: BrandlabSitemapConfig\n searchCertification: {\n representativeRoutes: {\n home: string\n contact?: string\n service?: string\n location?: string\n blog?: string\n }\n }\n exceptions?: readonly BrandlabException[]\n}\n\nconst PROJECT_PROFILES = new Set<BrandlabProjectProfile>([\n 'next-local-service',\n 'next-root-level',\n])\nconst ROUTE_FAMILIES = new Set<BrandlabRouteFamily>([\n 'services',\n 'locations',\n 'service-locations',\n 'blog',\n 'projects',\n 'legal',\n])\nconst BLOG_ADAPTERS = new Set<BrandlabBlogAdapter>(['none', 'mdx', 'structured'])\nconst ATTACHMENT_MODES = new Set<BrandlabContactAttachmentMode>([\n 'none',\n 'images-optional',\n 'images-required',\n])\nconst LOCATION_STRATEGIES = new Set<BrandlabLocationStrategy>([\n 'none',\n 'approved-service-area',\n 'physical-location',\n 'multi-location',\n])\nconst REVIEWS_PROVIDERS = new Set<BrandlabReviewsProvider>([\n 'google-places',\n 'project-owned',\n])\nconst GOOGLE_REVIEWS_MODES = new Set<BrandlabGoogleReviewsMode>([\n 'link-only',\n 'summary',\n 'full',\n])\nconst GOOGLE_REVIEWS_SELECTIONS = new Set<GooglePlaceReviewsSelection>([\n 'relevance',\n 'five-star-featured',\n])\n\nfunction assertNonEmpty(value: string, field: string) {\n if (!value.trim()) {\n throw new Error(`${field} cannot be empty.`)\n }\n}\n\nfunction assertRootRelativePath(path: string, field: string) {\n try {\n normalizeBrandlabRoutePath(path)\n } catch {\n throw new Error(`${field} must be a root-relative path without a query string or hash.`)\n }\n}\n\nfunction assertUnique(values: readonly string[], field: string) {\n const duplicate = values.find((value, index) => values.indexOf(value) !== index)\n if (duplicate) {\n throw new Error(`${field} contains duplicate value \"${duplicate}\".`)\n }\n}\n\n/**\n * Defines project-owned variation only. Runtime versions and global Brandlab\n * policy deliberately do not belong in this object.\n */\nexport function defineBrandlabProject<const T extends BrandlabProjectConfig>(config: T): T {\n assertNonEmpty(config.projectId, 'projectId')\n\n assertBrandlabCanonicalEntityId(config.projectId)\n if (!PROJECT_PROFILES.has(config.profile)) {\n throw new Error(`Unsupported project profile \"${String(config.profile)}\".`)\n }\n\n assertUnique(config.routes.families, 'routes.families')\n config.routes.families.forEach((family, index) => {\n if (!ROUTE_FAMILIES.has(family)) {\n throw new Error(`routes.families[${index}] is unsupported.`)\n }\n })\n\n const aliases = config.routes.aliases ?? []\n const normalizedAliases = aliases.map((alias) => ({\n ...alias,\n from: normalizeBrandlabRoutePath(alias.from),\n to: normalizeBrandlabRoutePath(alias.to),\n }))\n assertUnique(\n normalizedAliases.map((alias) => alias.from),\n 'routes.aliases.from',\n )\n\n aliases.forEach((alias, index) => {\n assertRootRelativePath(alias.from, `routes.aliases[${index}].from`)\n assertRootRelativePath(alias.to, `routes.aliases[${index}].to`)\n if (alias.status !== undefined && alias.status !== 301 && alias.status !== 308) {\n throw new Error(`routes.aliases[${index}].status must be 301 or 308.`)\n }\n if (normalizedAliases[index]!.from === normalizedAliases[index]!.to) {\n throw new Error(`routes.aliases[${index}] cannot redirect a path to itself.`)\n }\n })\n\n const aliasTargets = new Set(normalizedAliases.map((alias) => alias.to))\n const chainedAlias = normalizedAliases.find((alias) => aliasTargets.has(alias.from))\n if (chainedAlias) {\n throw new Error(`Route alias \"${chainedAlias.from}\" creates a redirect chain or cycle.`)\n }\n\n const representativeRoutes = config.searchCertification.representativeRoutes\n Object.entries(representativeRoutes).forEach(([name, path]) => {\n if (path) assertRootRelativePath(path, `searchCertification.representativeRoutes.${name}`)\n })\n if (representativeRoutes.home !== '/') {\n throw new Error('searchCertification.representativeRoutes.home must be \"/\".')\n }\n\n if (config.capabilities?.contact) {\n assertNonEmpty(config.capabilities.contact.fieldProfile, 'contact.fieldProfile')\n if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(config.capabilities.contact.fieldProfile)) {\n throw new Error('contact.fieldProfile must be lowercase kebab-case.')\n }\n if (!ATTACHMENT_MODES.has(config.capabilities.contact.attachments)) {\n throw new Error('contact.attachments is unsupported.')\n }\n }\n if (\n config.capabilities?.blog &&\n !BLOG_ADAPTERS.has(config.capabilities.blog.adapter)\n ) {\n throw new Error('blog.adapter is unsupported.')\n }\n if (\n config.capabilities?.locations &&\n !LOCATION_STRATEGIES.has(config.capabilities.locations.strategy)\n ) {\n throw new Error('locations.strategy is unsupported.')\n }\n if (\n config.capabilities?.reviews &&\n !REVIEWS_PROVIDERS.has(config.capabilities.reviews.provider)\n ) {\n throw new Error('reviews.provider is unsupported.')\n }\n if (\n config.capabilities?.reviews?.provider === 'google-places' &&\n !config.routes.families.includes('legal')\n ) {\n throw new Error('Google Places reviews require the legal route family.')\n }\n if (config.capabilities?.reviews?.provider === 'google-places') {\n const reviews = config.capabilities.reviews\n if (!GOOGLE_REVIEWS_MODES.has(reviews.mode)) {\n throw new Error('reviews.mode is unsupported.')\n }\n if (reviews.selection && !GOOGLE_REVIEWS_SELECTIONS.has(reviews.selection)) {\n throw new Error('reviews.selection is unsupported.')\n }\n if (reviews.selection === 'five-star-featured' && reviews.mode !== 'full') {\n throw new Error('Five-star featured reviews require full mode.')\n }\n if (!Array.isArray(reviews.routeIds) || reviews.routeIds.length === 0) {\n throw new Error('Google Places reviews require at least one approved route ID.')\n }\n assertUnique(reviews.routeIds, 'reviews.routeIds')\n reviews.routeIds.forEach((routeId, index) => {\n if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(routeId)) {\n throw new Error(`reviews.routeIds[${index}] must be lowercase kebab-case.`)\n }\n })\n }\n\n if (config.sitemap?.mode === 'split') {\n if (config.sitemap.families.length === 0) {\n throw new Error('Split sitemap configuration requires at least one family.')\n }\n assertUnique(config.sitemap.families, 'sitemap.families')\n config.sitemap.families.forEach((path, index) =>\n assertRootRelativePath(path, `sitemap.families[${index}]`),\n )\n assertNonEmpty(config.sitemap.reason, 'sitemap.reason')\n }\n\n assertUnique(\n (config.exceptions ?? []).map((exception) => exception.ruleId),\n 'exceptions.ruleId',\n )\n\n ;(config.exceptions ?? []).forEach((exception, index) => {\n assertNonEmpty(exception.ruleId, `exceptions[${index}].ruleId`)\n assertNonEmpty(exception.rationale, `exceptions[${index}].rationale`)\n assertNonEmpty(exception.evidence, `exceptions[${index}].evidence`)\n assertNonEmpty(exception.reviewTrigger, `exceptions[${index}].reviewTrigger`)\n if (exception.owner !== 'Romie') {\n throw new Error(`exceptions[${index}].owner must be \"Romie\".`)\n }\n\n if (exception.expiresAt && Number.isNaN(Date.parse(exception.expiresAt))) {\n throw new Error(`exceptions[${index}].expiresAt must be a valid date.`)\n }\n })\n\n return config\n}\n"]}
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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","sourcesContent":["import { normalizeBrandlabRoutePath } from './routes.js'\nimport { assertBrandlabCanonicalEntityId } from './entity-id.js'\nimport type { GooglePlaceReviewsSelection } from './google-reviews.js'\n\nexport type BrandlabProjectProfile =\n | 'next-local-service'\n | 'next-root-level'\n\nexport type BrandlabRouteFamily =\n | 'services'\n | 'locations'\n | 'service-locations'\n | 'blog'\n | 'projects'\n | 'legal'\n\nexport type BrandlabBlogAdapter = 'none' | 'mdx' | 'structured'\nexport type BrandlabContactAttachmentMode =\n | 'none'\n | 'images-optional'\n | 'images-required'\n | 'images-and-pdfs-optional'\nexport type BrandlabLocationStrategy =\n | 'none'\n | 'approved-service-area'\n | 'physical-location'\n | 'multi-location'\nexport type BrandlabReviewsProvider = 'google-places' | 'project-owned'\nexport type BrandlabGoogleReviewsMode = 'link-only' | 'summary' | 'full'\n\nexport type BrandlabReviewsCapability =\n | { provider: 'project-owned' }\n | {\n provider: 'google-places'\n mode: BrandlabGoogleReviewsMode\n /** Optional presentation policy. Five-star selection is valid only in full mode. */\n selection?: GooglePlaceReviewsSelection\n /** Stable route IDs where review proof is intentionally presented. */\n routeIds: readonly string[]\n }\n\nexport interface BrandlabRouteAlias {\n from: string\n to: string\n status?: 301 | 308\n}\n\nexport interface BrandlabException {\n ruleId: string\n rationale: string\n owner: 'Romie'\n evidence: string\n expiresAt?: string\n reviewTrigger: string\n}\n\nexport type BrandlabSitemapConfig =\n | { mode: 'single' }\n | {\n mode: 'split'\n families: readonly string[]\n reason: string\n }\n\nexport interface BrandlabProjectConfig {\n projectId: string\n profile: BrandlabProjectProfile\n routes: {\n families: readonly BrandlabRouteFamily[]\n aliases?: readonly BrandlabRouteAlias[]\n }\n capabilities?: {\n blog?: { adapter: BrandlabBlogAdapter }\n contact?: {\n fieldProfile: string\n attachments: BrandlabContactAttachmentMode\n }\n locations?: { strategy: BrandlabLocationStrategy }\n reviews?: BrandlabReviewsCapability\n }\n sitemap?: BrandlabSitemapConfig\n searchCertification: {\n representativeRoutes: {\n home: string\n contact?: string\n service?: string\n location?: string\n blog?: string\n }\n }\n exceptions?: readonly BrandlabException[]\n}\n\nconst PROJECT_PROFILES = new Set<BrandlabProjectProfile>([\n 'next-local-service',\n 'next-root-level',\n])\nconst ROUTE_FAMILIES = new Set<BrandlabRouteFamily>([\n 'services',\n 'locations',\n 'service-locations',\n 'blog',\n 'projects',\n 'legal',\n])\nconst BLOG_ADAPTERS = new Set<BrandlabBlogAdapter>(['none', 'mdx', 'structured'])\nconst ATTACHMENT_MODES = new Set<BrandlabContactAttachmentMode>([\n 'none',\n 'images-optional',\n 'images-required',\n 'images-and-pdfs-optional',\n])\nconst LOCATION_STRATEGIES = new Set<BrandlabLocationStrategy>([\n 'none',\n 'approved-service-area',\n 'physical-location',\n 'multi-location',\n])\nconst REVIEWS_PROVIDERS = new Set<BrandlabReviewsProvider>([\n 'google-places',\n 'project-owned',\n])\nconst GOOGLE_REVIEWS_MODES = new Set<BrandlabGoogleReviewsMode>([\n 'link-only',\n 'summary',\n 'full',\n])\nconst GOOGLE_REVIEWS_SELECTIONS = new Set<GooglePlaceReviewsSelection>([\n 'relevance',\n 'five-star-featured',\n])\n\nfunction assertNonEmpty(value: string, field: string) {\n if (!value.trim()) {\n throw new Error(`${field} cannot be empty.`)\n }\n}\n\nfunction assertRootRelativePath(path: string, field: string) {\n try {\n normalizeBrandlabRoutePath(path)\n } catch {\n throw new Error(`${field} must be a root-relative path without a query string or hash.`)\n }\n}\n\nfunction assertUnique(values: readonly string[], field: string) {\n const duplicate = values.find((value, index) => values.indexOf(value) !== index)\n if (duplicate) {\n throw new Error(`${field} contains duplicate value \"${duplicate}\".`)\n }\n}\n\n/**\n * Defines project-owned variation only. Runtime versions and global Brandlab\n * policy deliberately do not belong in this object.\n */\nexport function defineBrandlabProject<const T extends BrandlabProjectConfig>(config: T): T {\n assertNonEmpty(config.projectId, 'projectId')\n\n assertBrandlabCanonicalEntityId(config.projectId)\n if (!PROJECT_PROFILES.has(config.profile)) {\n throw new Error(`Unsupported project profile \"${String(config.profile)}\".`)\n }\n\n assertUnique(config.routes.families, 'routes.families')\n config.routes.families.forEach((family, index) => {\n if (!ROUTE_FAMILIES.has(family)) {\n throw new Error(`routes.families[${index}] is unsupported.`)\n }\n })\n\n const aliases = config.routes.aliases ?? []\n const normalizedAliases = aliases.map((alias) => ({\n ...alias,\n from: normalizeBrandlabRoutePath(alias.from),\n to: normalizeBrandlabRoutePath(alias.to),\n }))\n assertUnique(\n normalizedAliases.map((alias) => alias.from),\n 'routes.aliases.from',\n )\n\n aliases.forEach((alias, index) => {\n assertRootRelativePath(alias.from, `routes.aliases[${index}].from`)\n assertRootRelativePath(alias.to, `routes.aliases[${index}].to`)\n if (alias.status !== undefined && alias.status !== 301 && alias.status !== 308) {\n throw new Error(`routes.aliases[${index}].status must be 301 or 308.`)\n }\n if (normalizedAliases[index]!.from === normalizedAliases[index]!.to) {\n throw new Error(`routes.aliases[${index}] cannot redirect a path to itself.`)\n }\n })\n\n const aliasTargets = new Set(normalizedAliases.map((alias) => alias.to))\n const chainedAlias = normalizedAliases.find((alias) => aliasTargets.has(alias.from))\n if (chainedAlias) {\n throw new Error(`Route alias \"${chainedAlias.from}\" creates a redirect chain or cycle.`)\n }\n\n const representativeRoutes = config.searchCertification.representativeRoutes\n Object.entries(representativeRoutes).forEach(([name, path]) => {\n if (path) assertRootRelativePath(path, `searchCertification.representativeRoutes.${name}`)\n })\n if (representativeRoutes.home !== '/') {\n throw new Error('searchCertification.representativeRoutes.home must be \"/\".')\n }\n\n if (config.capabilities?.contact) {\n assertNonEmpty(config.capabilities.contact.fieldProfile, 'contact.fieldProfile')\n if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(config.capabilities.contact.fieldProfile)) {\n throw new Error('contact.fieldProfile must be lowercase kebab-case.')\n }\n if (!ATTACHMENT_MODES.has(config.capabilities.contact.attachments)) {\n throw new Error('contact.attachments is unsupported.')\n }\n }\n if (\n config.capabilities?.blog &&\n !BLOG_ADAPTERS.has(config.capabilities.blog.adapter)\n ) {\n throw new Error('blog.adapter is unsupported.')\n }\n if (\n config.capabilities?.locations &&\n !LOCATION_STRATEGIES.has(config.capabilities.locations.strategy)\n ) {\n throw new Error('locations.strategy is unsupported.')\n }\n if (\n config.capabilities?.reviews &&\n !REVIEWS_PROVIDERS.has(config.capabilities.reviews.provider)\n ) {\n throw new Error('reviews.provider is unsupported.')\n }\n if (\n config.capabilities?.reviews?.provider === 'google-places' &&\n !config.routes.families.includes('legal')\n ) {\n throw new Error('Google Places reviews require the legal route family.')\n }\n if (config.capabilities?.reviews?.provider === 'google-places') {\n const reviews = config.capabilities.reviews\n if (!GOOGLE_REVIEWS_MODES.has(reviews.mode)) {\n throw new Error('reviews.mode is unsupported.')\n }\n if (reviews.selection && !GOOGLE_REVIEWS_SELECTIONS.has(reviews.selection)) {\n throw new Error('reviews.selection is unsupported.')\n }\n if (reviews.selection === 'five-star-featured' && reviews.mode !== 'full') {\n throw new Error('Five-star featured reviews require full mode.')\n }\n if (!Array.isArray(reviews.routeIds) || reviews.routeIds.length === 0) {\n throw new Error('Google Places reviews require at least one approved route ID.')\n }\n assertUnique(reviews.routeIds, 'reviews.routeIds')\n reviews.routeIds.forEach((routeId, index) => {\n if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(routeId)) {\n throw new Error(`reviews.routeIds[${index}] must be lowercase kebab-case.`)\n }\n })\n }\n\n if (config.sitemap?.mode === 'split') {\n if (config.sitemap.families.length === 0) {\n throw new Error('Split sitemap configuration requires at least one family.')\n }\n assertUnique(config.sitemap.families, 'sitemap.families')\n config.sitemap.families.forEach((path, index) =>\n assertRootRelativePath(path, `sitemap.families[${index}]`),\n )\n assertNonEmpty(config.sitemap.reason, 'sitemap.reason')\n }\n\n assertUnique(\n (config.exceptions ?? []).map((exception) => exception.ruleId),\n 'exceptions.ruleId',\n )\n\n ;(config.exceptions ?? []).forEach((exception, index) => {\n assertNonEmpty(exception.ruleId, `exceptions[${index}].ruleId`)\n assertNonEmpty(exception.rationale, `exceptions[${index}].rationale`)\n assertNonEmpty(exception.evidence, `exceptions[${index}].evidence`)\n assertNonEmpty(exception.reviewTrigger, `exceptions[${index}].reviewTrigger`)\n if (exception.owner !== 'Romie') {\n throw new Error(`exceptions[${index}].owner must be \"Romie\".`)\n }\n\n if (exception.expiresAt && Number.isNaN(Date.parse(exception.expiresAt))) {\n throw new Error(`exceptions[${index}].expiresAt must be a valid date.`)\n }\n })\n\n return config\n}\n"]}
|