What
- Add
mailer.brand with these fields:
name, falling back to app.title
url, falling back to app.url || getBaseUrl()
contact, falling back to app.contact
logoUrl, primaryColor, textColor, mutedColor, fontFamily, signature, footerText
links as a { label: url } object (arrays replace on deep merge)
- Export a
getBrand() getter as the single source of the resolved values.
render injects brand, plus the legacy appName (= brand.name) and appContact (= brand.contact). Brand wins over caller-passed values for those two keys only.
Acceptance criteria
Tests
- Fallbacks with an empty brand.
- An override from a mocked config shaped like a downstream override.
- Brand winning over a caller-passed
appName.
- A
deepMerge check with a partial brand override.
Depends on: resolver (render).
What
mailer.brandwith these fields:name, falling back toapp.titleurl, falling back toapp.url || getBaseUrl()contact, falling back toapp.contactlogoUrl,primaryColor,textColor,mutedColor,fontFamily,signature,footerTextlinksas a{ label: url }object (arrays replace on deep merge)getBrand()getter as the single source of the resolved values.renderinjectsbrand, plus the legacyappName(=brand.name) andappContact(=brand.contact). Brand wins over caller-passed values for those two keys only.Acceptance criteria
mailer.brand.namechanges{{appName}}in every mail, including templates that exist only in a downstream.DEVKIT_NODE_mailer_brand_<field>env vars set brand values.Tests
appName.deepMergecheck with a partial brand override.Depends on: resolver (
render).