@samva/markup 0.10.0 → 0.11.1

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/AGENTS.md CHANGED
@@ -30,7 +30,11 @@ Authoring entries are what a template file imports:
30
30
  mode) and `BrandFooter` (company, postal address, social links and the unsubscribe row the send
31
31
  path fills). The compiler binds them to the brand the build resolves; add
32
32
  `"types": ["@samva/markup/samva-brand"]` to the project's tsconfig for `tsc`. In `theme.css`,
33
- `@import "samva:brand";` layers the brand theme; `samva:brand/<slug>` names one brand.
33
+ `@import "samva:brand";` layers the brand theme; `samva:brand/<slug>` names one brand. These are
34
+ Samva's brand plugin (`samvaBrandPlugin` from `@samva/markup/brand`), the default; a host that
35
+ delivers mail itself passes its own `BrandPlugin` as `brandPlugin` to `compileTemplate`,
36
+ `renderIr` and `renderBrandFooter` to rename the brand import, the footer markers, the
37
+ unsubscribe placeholder and the unsubscribe link's no-track marker.
34
38
 
35
39
  Tooling entries are what a host (Vite, the CLI, the editor, the API, the template agent) imports:
36
40
 
package/CHANGELOG.md CHANGED
@@ -1,3 +1,37 @@
1
+ ## @samva/markup@0.11.0
2
+
3
+ ### Breaking: `@samva/markup` reads brand and footer names from a brand plugin
4
+
5
+ Templates do not change: `samva:brand`, `BrandLogo`, `BrandFooter` and the unsubscribe row work as
6
+ before, because Samva's brand plugin is the default.
7
+
8
+ A host that compiles or renders templates can pass its own `BrandPlugin` as `brandPlugin` to
9
+ `compileTemplate`, `transformProject`, `projectBrandSpecifier`, `parseBrandSpecifier`,
10
+ `checkStaticProfile`, `renderIr`, `compileEmail`, `renderBrandFooter`, `renderBrandSample`,
11
+ `brandComponents`, `renderedFooterMarkers` and `withoutUnsubscribeRow`. The plugin names the brand
12
+ import, the attributes that mark the footer and its unsubscribe row, the unsubscribe link's URL
13
+ placeholder, and the attribute that marks that link as untracked.
14
+
15
+ `BrandFooter`'s unsubscribe link now carries `data-samva-no-track` instead of SES's
16
+ `ses:no-track`. A host that delivers mail through SES turns the marker into `ses:no-track` before it
17
+ sends.
18
+
19
+ `@samva/markup/brand` exports `samvaBrandPlugin` and the `BrandPlugin` type in place of
20
+ `FOOTER_ATTRIBUTE`, `UNSUBSCRIBE_ROW_ATTRIBUTE` and `UNSUBSCRIBE_URL_PLACEHOLDER`; read
21
+ `samvaBrandPlugin.footerAttribute`, `.unsubscribeRowAttribute` and `.unsubscribeUrlPlaceholder`.
22
+ `@samva/markup/compiler` no longer exports `BRAND_SPECIFIER`; read `samvaBrandPlugin.specifier`.
23
+
24
+ ### Breaking: supply brand resolution to the authoring toolchain
25
+
26
+ `@samva/vite` accepts `brandResolver` and `brandPlugin` on the editor plugin and its dev, project,
27
+ build, render and export APIs. A brand import without a resolver fails with `brand-unavailable`.
28
+ Standalone callers supply a resolver; the package does not read Samva credentials, invoke the
29
+ Samva CLI or load Samva brand snapshots. The `offline` option and Samva snapshot helpers move to
30
+ the CLI.
31
+
32
+ `samva templates dev`, `check`, `build`, `render`, `snapshot` and `export` supply the Samva resolver
33
+ and brand plugin, preserving live lookup, cached snapshots and brand diagnostics.
34
+
1
35
  ## @samva/markup@0.10.0
2
36
 
3
37
  ### Breaking: `@samva/markup` publishes 17 entries in two tiers
package/README.md CHANGED
@@ -3,6 +3,17 @@
3
3
  Type-safe authoring and compilation primitives for Samva email, SMS, and WhatsApp templates. Use it
4
4
  directly for TSX templates or together with `@samva/vite` for the visual editor and project build.
5
5
 
6
+ ## For coding agents
7
+
8
+ - **SML** is the name of this toolkit, not a second syntax. An email is a TSX file that
9
+ default-exports `defineTemplate`, written in the static profile (ordinary JSX, HTML, and Tailwind
10
+ classes read without running). Compiling it produces the **IR**, the JSON description that
11
+ previews and sends render.
12
+ - The package ships the `sml` skill at `skills/sml/SKILL.md`; it routes you through write, check,
13
+ render, look, and repair. `AGENTS.md` and `contract.json` ship alongside it.
14
+ - Run `samva templates check --json` after every edit for diagnostics with `file:line:column` and a
15
+ fix for each.
16
+
6
17
  ## Install
7
18
 
8
19
  ```sh
@@ -1,3 +1,4 @@
1
+ import { t as BrandPlugin } from "./brand-plugin-CHhcei-j.js";
1
2
  import { r as EmailDiagnostic } from "./diagnostics-B-Gym34R.js";
2
3
  import { r as EmailNode } from "./jsx-runtime-ylIOnaSf.js";
3
4
  //#region src/email/brand.d.ts
@@ -24,17 +25,13 @@ interface EmailBrandFooter {
24
25
  readonly socialLinks?: readonly EmailBrandSocialLink[] | undefined;
25
26
  }
26
27
  interface EmailBrand {
27
- /** The brand's slug; `samva:brand/<slug>` must name it. */
28
+ /** The brand's slug; a `<specifier>/<slug>` brand import must name it. */
28
29
  readonly slug: string;
29
30
  /** Brand theme CSS in the closed grammar `parseBrandCss` accepts. */
30
31
  readonly css: string;
31
32
  readonly assets?: EmailBrandAssets | undefined;
32
33
  readonly footer?: EmailBrandFooter | undefined;
33
34
  }
34
- /** Marks the block the send path recognizes as the template's own footer. */
35
- declare const FOOTER_ATTRIBUTE = "data-samva-footer";
36
- /** Marks the footer row that carries the unsubscribe link. */
37
- declare const UNSUBSCRIBE_ROW_ATTRIBUTE = "data-samva-unsubscribe";
38
35
  /** Every class the brand components use, compiled for any project that imports them. */
39
36
  declare const BRAND_COMPONENT_CLASSES: readonly ["dark:hidden!", "hidden", "dark:block!", "text-xs", "text-gray-500", "text-center", "font-semibold", "underline"];
40
37
  interface BrandLogoProps {
@@ -54,8 +51,8 @@ interface BrandComponents {
54
51
  readonly BrandLogo: (props: BrandLogoProps) => EmailNode | null;
55
52
  readonly BrandFooter: (props: BrandFooterProps) => EmailNode | null;
56
53
  }
57
- /** Bind `BrandLogo` and `BrandFooter` to one brand. */
58
- declare const brandComponents: (brand: EmailBrand) => BrandComponents;
54
+ /** Bind `BrandLogo` and `BrandFooter` to one brand, marked as the plugin names. */
55
+ declare const brandComponents: (brand: EmailBrand, plugin?: BrandPlugin) => BrandComponents;
59
56
  /** What a rendered message says about the footer it carries. */
60
57
  interface RenderedFooterMarkers {
61
58
  /** The message renders `BrandFooter` (or claims to, with the marker). */
@@ -63,8 +60,8 @@ interface RenderedFooterMarkers {
63
60
  /** The message carries the unsubscribe placeholder link only `BrandFooter` can write. */
64
61
  readonly unsubscribeLink: boolean;
65
62
  }
66
- /** Read the footer markers from rendered HTML. */
67
- declare const renderedFooterMarkers: (html: string) => RenderedFooterMarkers;
63
+ /** Read the plugin's footer markers from rendered HTML. */
64
+ declare const renderedFooterMarkers: (html: string, plugin?: BrandPlugin) => RenderedFooterMarkers;
68
65
  /**
69
66
  * Drop the unsubscribe row from a rendered message, for a send that resolved
70
67
  * no unsubscribe group: there is no link to put there, so the row goes.
@@ -72,7 +69,7 @@ declare const renderedFooterMarkers: (html: string) => RenderedFooterMarkers;
72
69
  declare const withoutUnsubscribeRow: (rendered: {
73
70
  readonly html?: string | undefined;
74
71
  readonly text?: string | undefined;
75
- }) => {
72
+ }, plugin?: BrandPlugin) => {
76
73
  readonly html: string | undefined;
77
74
  readonly text: string | undefined;
78
75
  };
@@ -114,5 +111,5 @@ declare const isExternalAsset: (reference: string) => boolean;
114
111
  */
115
112
  declare const checkAssetBase: (base: string) => readonly EmailDiagnostic[];
116
113
  //#endregion
117
- export { withoutUnsubscribeRow as C, renderedFooterMarkers as S, EmailBrandSocialLink as _, assetUrl as a, UNSUBSCRIBE_ROW_ATTRIBUTE as b, isExternalAsset as c, BrandFooterProps as d, BrandLogoProps as f, EmailBrandFooter as g, EmailBrandAssets as h, assetEntry as i, BRAND_COMPONENT_CLASSES as l, EmailBrandAddress as m, AssetEntry as n, checkAssetBase as o, EmailBrand as p, AssetManifest as r, isAssetPath as s, ASSET_CONTENT_TYPES as t, BrandComponents as u, FOOTER_ATTRIBUTE as v, brandComponents as x, RenderedFooterMarkers as y };
118
- //# sourceMappingURL=assets-BIsyMQ92.d.ts.map
114
+ export { EmailBrandSocialLink as _, assetUrl as a, renderedFooterMarkers as b, isExternalAsset as c, BrandFooterProps as d, BrandLogoProps as f, EmailBrandFooter as g, EmailBrandAssets as h, assetEntry as i, BRAND_COMPONENT_CLASSES as l, EmailBrandAddress as m, AssetEntry as n, checkAssetBase as o, EmailBrand as p, AssetManifest as r, isAssetPath as s, ASSET_CONTENT_TYPES as t, BrandComponents as u, RenderedFooterMarkers as v, withoutUnsubscribeRow as x, brandComponents as y };
115
+ //# sourceMappingURL=assets-B0yseju_.d.ts.map
@@ -1,4 +1,4 @@
1
- import { i as UNSUBSCRIBE_URL_PLACEHOLDER, r as UNSUBSCRIBE_LINK, t as BRAND_FOOTER_MARKER } from "./unsubscribe-link-CoesnQDU.js";
1
+ import { D as samvaBrandPlugin, O as BRAND_FOOTER_MARKER, k as UNSUBSCRIBE_LINK, r as escapeAttribute } from "./html-DjPxwjLi.js";
2
2
  import { a as jsx, n as Fragment } from "./jsx-runtime-CBlexoeR.js";
3
3
  //#region src/email/assets.ts
4
4
  /** Files a template may import, and what they are served as. */
@@ -61,10 +61,6 @@ const checkAssetBase = (base) => /^(https:\/\/|\.{1,2}\/|\/)/i.test(base) ? [] :
61
61
  }];
62
62
  //#endregion
63
63
  //#region src/email/brand.ts
64
- /** Marks the block the send path recognizes as the template's own footer. */
65
- const FOOTER_ATTRIBUTE = "data-samva-footer";
66
- /** Marks the footer row that carries the unsubscribe link. */
67
- const UNSUBSCRIBE_ROW_ATTRIBUTE = "data-samva-unsubscribe";
68
64
  /** Every class the brand components use, compiled for any project that imports them. */
69
65
  const BRAND_COMPONENT_CLASSES = [
70
66
  "dark:hidden!",
@@ -97,8 +93,8 @@ const row = (children, props = {}) => jsx("tr", {
97
93
  children
98
94
  })
99
95
  });
100
- /** Bind `BrandLogo` and `BrandFooter` to one brand. */
101
- const brandComponents = (brand) => {
96
+ /** Bind `BrandLogo` and `BrandFooter` to one brand, marked as the plugin names. */
97
+ const brandComponents = (brand, plugin = samvaBrandPlugin) => {
102
98
  const assets = brand.assets ?? {};
103
99
  const footer = brand.footer ?? {};
104
100
  const BrandLogo = ({ width = 120, alt, align, className }) => {
@@ -145,8 +141,8 @@ const brandComponents = (brand) => {
145
141
  className: "text-gray-500 underline",
146
142
  children: link.label
147
143
  })])));
148
- rows.push(row(unsubscribeLink(unsubscribeLabel), {
149
- [UNSUBSCRIBE_ROW_ATTRIBUTE]: "",
144
+ rows.push(row(unsubscribeLink(unsubscribeLabel, plugin), {
145
+ [plugin.unsubscribeRowAttribute]: "",
150
146
  [BRAND_FOOTER_MARKER]: true
151
147
  }));
152
148
  return jsx("table", {
@@ -155,7 +151,7 @@ const brandComponents = (brand) => {
155
151
  border: 0,
156
152
  cellpadding: 0,
157
153
  cellspacing: 0,
158
- [FOOTER_ATTRIBUTE]: "",
154
+ [plugin.footerAttribute]: "",
159
155
  [BRAND_FOOTER_MARKER]: true,
160
156
  children: jsx("tbody", { children: jsx("tr", { children: jsx("td", {
161
157
  align: "center",
@@ -178,31 +174,29 @@ const brandComponents = (brand) => {
178
174
  };
179
175
  /**
180
176
  * The unsubscribe link. Its href is the placeholder, which no author-written
181
- * link may carry: the serializer accepts it only on this node, and marks it so
182
- * click tracking never rewrites it.
177
+ * link may carry: the serializer accepts it only on this node, and writes the
178
+ * plugin's no-track marker on it so click tracking never rewrites it.
183
179
  */
184
- const unsubscribeLink = (label) => jsx("a", {
185
- href: UNSUBSCRIBE_URL_PLACEHOLDER,
180
+ const unsubscribeLink = (label, plugin) => jsx("a", {
181
+ href: plugin.unsubscribeUrlPlaceholder,
186
182
  className: "text-gray-500 underline",
187
183
  [UNSUBSCRIBE_LINK]: true,
188
184
  children: label
189
185
  });
190
- const UNSUBSCRIBE_ROW = new RegExp(`<tr ${UNSUBSCRIBE_ROW_ATTRIBUTE}=""[^>]*>[\\s\\S]*?</tr>`, "g");
191
- const UNSUBSCRIBE_HREF = `href="${UNSUBSCRIBE_URL_PLACEHOLDER}"`;
192
- /** Read the footer markers from rendered HTML. */
193
- const renderedFooterMarkers = (html) => ({
194
- footer: html.includes(`${FOOTER_ATTRIBUTE}=""`),
195
- unsubscribeLink: html.includes(UNSUBSCRIBE_HREF)
186
+ /** Read the plugin's footer markers from rendered HTML. */
187
+ const renderedFooterMarkers = (html, plugin = samvaBrandPlugin) => ({
188
+ footer: html.includes(`${plugin.footerAttribute}=""`),
189
+ unsubscribeLink: html.includes(`href="${escapeAttribute(plugin.unsubscribeUrlPlaceholder)}"`)
196
190
  });
197
191
  /**
198
192
  * Drop the unsubscribe row from a rendered message, for a send that resolved
199
193
  * no unsubscribe group: there is no link to put there, so the row goes.
200
194
  */
201
- const withoutUnsubscribeRow = (rendered) => ({
202
- html: rendered.html?.replace(UNSUBSCRIBE_ROW, ""),
203
- text: rendered.text?.split("\n").filter((line) => !line.includes(UNSUBSCRIBE_URL_PLACEHOLDER)).join("\n")
195
+ const withoutUnsubscribeRow = (rendered, plugin = samvaBrandPlugin) => ({
196
+ html: rendered.html?.replace(new RegExp(`<tr ${plugin.unsubscribeRowAttribute}=""[^>]*>[\\s\\S]*?</tr>`, "g"), ""),
197
+ text: rendered.text?.split("\n").filter((line) => !line.includes(plugin.unsubscribeUrlPlaceholder)).join("\n")
204
198
  });
205
199
  //#endregion
206
- export { renderedFooterMarkers as a, assetEntry as c, isAssetPath as d, isExternalAsset as f, brandComponents as i, assetUrl as l, FOOTER_ATTRIBUTE as n, withoutUnsubscribeRow as o, UNSUBSCRIBE_ROW_ATTRIBUTE as r, ASSET_CONTENT_TYPES as s, BRAND_COMPONENT_CLASSES as t, checkAssetBase as u };
200
+ export { ASSET_CONTENT_TYPES as a, checkAssetBase as c, withoutUnsubscribeRow as i, isAssetPath as l, brandComponents as n, assetEntry as o, renderedFooterMarkers as r, assetUrl as s, BRAND_COMPONENT_CLASSES as t, isExternalAsset as u };
207
201
 
208
- //# sourceMappingURL=brand-C0MKTwmX.js.map
202
+ //# sourceMappingURL=brand-DTctcDmf.js.map
@@ -0,0 +1,32 @@
1
+ //#region src/email/brand-plugin.d.ts
2
+ /**
3
+ * How a template reaches its brand and how the footer `BrandFooter` renders is
4
+ * marked for the send path that delivers it. The compiler and the IR renderer
5
+ * read every brand import, footer marker and unsubscribe link from the plugin
6
+ * a build passes; `samvaBrandPlugin` is the default.
7
+ */
8
+ interface BrandPlugin {
9
+ /**
10
+ * The module and stylesheet specifier a template imports its brand from.
11
+ * `<specifier>/<slug>` names one brand; the bare specifier names the default.
12
+ */
13
+ readonly specifier: string;
14
+ /** Marks the block `BrandFooter` renders, so the send path finds a template's own footer. */
15
+ readonly footerAttribute: `data-${string}`;
16
+ /** Marks the footer row that carries the unsubscribe link, so a send can drop it. */
17
+ readonly unsubscribeRowAttribute: `data-${string}`;
18
+ /** The unsubscribe link's href; the send path substitutes each delivery's URL for it. */
19
+ readonly unsubscribeUrlPlaceholder: string;
20
+ /**
21
+ * Written on the unsubscribe link, so the send path delivers it without
22
+ * click tracking: a rewritten link would carry another recipient's token.
23
+ */
24
+ readonly noTrackAttribute: `data-${string}`;
25
+ }
26
+ /** The brand a specifier names: `undefined` slug means the default brand; `undefined` when it names none. */
27
+ declare const parseBrandSpecifier: (specifier: string, plugin?: BrandPlugin) => {
28
+ readonly slug: string | undefined;
29
+ } | undefined;
30
+ //#endregion
31
+ export { parseBrandSpecifier as n, BrandPlugin as t };
32
+ //# sourceMappingURL=brand-plugin-CHhcei-j.d.ts.map
package/dist/brand.d.ts CHANGED
@@ -1,13 +1,15 @@
1
- import { C as withoutUnsubscribeRow, S as renderedFooterMarkers, _ as EmailBrandSocialLink, a as assetUrl, b as UNSUBSCRIBE_ROW_ATTRIBUTE, c as isExternalAsset, d as BrandFooterProps, f as BrandLogoProps, g as EmailBrandFooter, h as EmailBrandAssets, i as assetEntry, l as BRAND_COMPONENT_CLASSES, m as EmailBrandAddress, n as AssetEntry, o as checkAssetBase, p as EmailBrand, r as AssetManifest, s as isAssetPath, t as ASSET_CONTENT_TYPES, u as BrandComponents, v as FOOTER_ATTRIBUTE, x as brandComponents, y as RenderedFooterMarkers } from "./assets-BIsyMQ92.js";
1
+ import { t as BrandPlugin } from "./brand-plugin-CHhcei-j.js";
2
+ import { _ as EmailBrandSocialLink, a as assetUrl, b as renderedFooterMarkers, c as isExternalAsset, d as BrandFooterProps, f as BrandLogoProps, g as EmailBrandFooter, h as EmailBrandAssets, i as assetEntry, l as BRAND_COMPONENT_CLASSES, m as EmailBrandAddress, n as AssetEntry, o as checkAssetBase, p as EmailBrand, r as AssetManifest, s as isAssetPath, t as ASSET_CONTENT_TYPES, u as BrandComponents, v as RenderedFooterMarkers, x as withoutUnsubscribeRow, y as brandComponents } from "./assets-B0yseju_.js";
2
3
  import { t as Diagnostic } from "./diagnostic-model-DxthUZBT.js";
3
4
  import { Result } from "better-result";
4
- //#region src/internal/unsubscribe-link.d.ts
5
+ //#region src/email/samva-brand-plugin.d.ts
5
6
  /**
6
- * The per-recipient unsubscribe URL is not known at render: the send path
7
- * substitutes each delivery's link for this placeholder, or removes the
8
- * unsubscribe row when the send resolved no unsubscribe group.
7
+ * Samva's brand plugin, the compiler's default. A template imports its
8
+ * organization's brand from `samva:brand`, and Samva's send path fills
9
+ * `%samva:unsubscribe-url%` per delivery and turns `data-samva-no-track` into
10
+ * its email provider's own no-track marker.
9
11
  */
10
- declare const UNSUBSCRIBE_URL_PLACEHOLDER = "%samva:unsubscribe-url%";
12
+ declare const samvaBrandPlugin: BrandPlugin;
11
13
  //#endregion
12
14
  //#region src/theme-css.d.ts
13
15
  /** One `@font-face` a brand declares. `src` is the declaration value as authored. */
@@ -67,8 +69,8 @@ declare const brandForegroundDefaults: (brand: BrandCss) => {
67
69
  * build compiles the same default and the stored CSS never carries it.
68
70
  */
69
71
  declare const brandThemeVariables: (brand: BrandCss) => Readonly<Record<string, string>>;
70
- /** The stylesheet Tailwind compiles for `@import "samva:brand"`: `brandThemeVariables` and the font faces. */
72
+ /** The stylesheet Tailwind compiles for the theme's brand import: `brandThemeVariables` and the font faces. */
71
73
  declare const brandTailwindCss: (brand: BrandCss) => string;
72
74
  //#endregion
73
- export { ASSET_CONTENT_TYPES, AssetEntry, AssetManifest, BRAND_COLOR_VARIABLE, BRAND_COMPONENT_CLASSES, BRAND_FOREGROUND_VARIABLE, BrandComponents, BrandCss, BrandFontFace, BrandFooterProps, BrandLogoProps, EmailBrand, EmailBrandAddress, EmailBrandAssets, EmailBrandFooter, EmailBrandSocialLink, FOOTER_ATTRIBUTE, RenderedFooterMarkers, UNSUBSCRIBE_ROW_ATTRIBUTE, UNSUBSCRIBE_URL_PLACEHOLDER, assetEntry, assetUrl, brandComponents, brandForegroundDefaults, brandTailwindCss, brandThemeVariables, checkAssetBase, contrastForeground, darkVariableName, isAssetPath, isExternalAsset, parseBrandCss, printBrandCss, renderedFooterMarkers, withoutUnsubscribeRow };
75
+ export { ASSET_CONTENT_TYPES, AssetEntry, AssetManifest, BRAND_COLOR_VARIABLE, BRAND_COMPONENT_CLASSES, BRAND_FOREGROUND_VARIABLE, BrandComponents, BrandCss, BrandFontFace, BrandFooterProps, BrandLogoProps, type BrandPlugin, EmailBrand, EmailBrandAddress, EmailBrandAssets, EmailBrandFooter, EmailBrandSocialLink, RenderedFooterMarkers, assetEntry, assetUrl, brandComponents, brandForegroundDefaults, brandTailwindCss, brandThemeVariables, checkAssetBase, contrastForeground, darkVariableName, isAssetPath, isExternalAsset, parseBrandCss, printBrandCss, renderedFooterMarkers, samvaBrandPlugin, withoutUnsubscribeRow };
74
76
  //# sourceMappingURL=brand.d.ts.map
package/dist/brand.js CHANGED
@@ -1,4 +1,4 @@
1
- import { i as UNSUBSCRIBE_URL_PLACEHOLDER } from "./unsubscribe-link-CoesnQDU.js";
2
- import { a as brandThemeVariables, c as parseBrandCss, i as brandTailwindCss, l as printBrandCss, n as BRAND_FOREGROUND_VARIABLE, o as contrastForeground, r as brandForegroundDefaults, s as darkVariableName, t as BRAND_COLOR_VARIABLE } from "./theme-css-JtRCm1fb.js";
3
- import { a as renderedFooterMarkers, c as assetEntry, d as isAssetPath, f as isExternalAsset, i as brandComponents, l as assetUrl, n as FOOTER_ATTRIBUTE, o as withoutUnsubscribeRow, r as UNSUBSCRIBE_ROW_ATTRIBUTE, s as ASSET_CONTENT_TYPES, t as BRAND_COMPONENT_CLASSES, u as checkAssetBase } from "./brand-C0MKTwmX.js";
4
- export { ASSET_CONTENT_TYPES, BRAND_COLOR_VARIABLE, BRAND_COMPONENT_CLASSES, BRAND_FOREGROUND_VARIABLE, FOOTER_ATTRIBUTE, UNSUBSCRIBE_ROW_ATTRIBUTE, UNSUBSCRIBE_URL_PLACEHOLDER, assetEntry, assetUrl, brandComponents, brandForegroundDefaults, brandTailwindCss, brandThemeVariables, checkAssetBase, contrastForeground, darkVariableName, isAssetPath, isExternalAsset, parseBrandCss, printBrandCss, renderedFooterMarkers, withoutUnsubscribeRow };
1
+ import { D as samvaBrandPlugin } from "./html-DjPxwjLi.js";
2
+ import { a as brandThemeVariables, c as parseBrandCss, i as brandTailwindCss, l as printBrandCss, n as BRAND_FOREGROUND_VARIABLE, o as contrastForeground, r as brandForegroundDefaults, s as darkVariableName, t as BRAND_COLOR_VARIABLE } from "./theme-css-CEUrDpqq.js";
3
+ import { a as ASSET_CONTENT_TYPES, c as checkAssetBase, i as withoutUnsubscribeRow, l as isAssetPath, n as brandComponents, o as assetEntry, r as renderedFooterMarkers, s as assetUrl, t as BRAND_COMPONENT_CLASSES, u as isExternalAsset } from "./brand-DTctcDmf.js";
4
+ export { ASSET_CONTENT_TYPES, BRAND_COLOR_VARIABLE, BRAND_COMPONENT_CLASSES, BRAND_FOREGROUND_VARIABLE, assetEntry, assetUrl, brandComponents, brandForegroundDefaults, brandTailwindCss, brandThemeVariables, checkAssetBase, contrastForeground, darkVariableName, isAssetPath, isExternalAsset, parseBrandCss, printBrandCss, renderedFooterMarkers, samvaBrandPlugin, withoutUnsubscribeRow };
@@ -1,7 +1,8 @@
1
+ import { n as parseBrandSpecifier, t as BrandPlugin } from "./brand-plugin-CHhcei-j.js";
1
2
  import { a as TemplateDiagnostic, r as EmailDiagnostic } from "./diagnostics-B-Gym34R.js";
2
3
  import { r as EmailNode } from "./jsx-runtime-ylIOnaSf.js";
3
- import { n as AssetEntry, p as EmailBrand, u as BrandComponents } from "./assets-BIsyMQ92.js";
4
- import { C as Compound, D as EmittedPosition, S as CompiledSelector, T as matchesSelector, _ as Specificity, b as parseStylesheet, ct as TemplateIr, g as CssRule, h as CssDeclaration, r as RenderedEmail, v as Stylesheet, w as classListOf, x as Ancestry, y as compileSelector } from "./render-BPonDpCl.js";
4
+ import { n as AssetEntry, p as EmailBrand, u as BrandComponents } from "./assets-B0yseju_.js";
5
+ import { C as Compound, D as EmittedPosition, S as CompiledSelector, T as matchesSelector, _ as Specificity, b as parseStylesheet, ct as TemplateIr, g as CssRule, h as CssDeclaration, r as RenderedEmail, v as Stylesheet, w as classListOf, x as Ancestry, y as compileSelector } from "./render-DQKz4Aco.js";
5
6
  import { a as TEMPLATE_NAME_PATTERN, n as EmailContent } from "./template-DMPwxMSq.js";
6
7
  import { caniemail } from "caniemail";
7
8
  //#region src/email/compatibility.d.ts
@@ -156,12 +157,14 @@ interface TailwindOutput {
156
157
  }
157
158
  /**
158
159
  * What the project theme's `@import`s resolve to, already checked by
159
- * `resolveThemeImports`: project stylesheets by path, and the brand theme
160
- * `samva:brand` stands for.
160
+ * `resolveThemeImports`: project stylesheets by path, and the brand theme the
161
+ * brand plugin's specifier stands for.
161
162
  */
162
163
  interface TailwindImports {
163
164
  readonly stylesheets: ReadonlyMap<string, string>;
164
165
  readonly brandCss?: string | undefined;
166
+ /** Names the brand import; `samvaBrandPlugin` when omitted. */
167
+ readonly brandPlugin?: BrandPlugin | undefined;
165
168
  }
166
169
  /** Compile the discovered class set into CSS with the pinned Tailwind release. */
167
170
  declare const compileTailwind: (candidates: Iterable<string>, options?: TailwindOptions, imports?: TailwindImports) => Promise<TailwindOutput>;
@@ -182,10 +185,12 @@ interface TransformOptions {
182
185
  /** `false` compiles no Tailwind at all. */
183
186
  readonly tailwind?: TailwindOptions | false | undefined;
184
187
  /**
185
- * The brand `samva:brand` resolves to, supplied by the host. A project that
186
- * imports `samva:brand` with no brand here fails to compile.
188
+ * The brand the brand import resolves to, supplied by the host. A project
189
+ * that imports a brand with no brand here fails to compile.
187
190
  */
188
191
  readonly brand?: EmailBrand | undefined;
192
+ /** Names the brand import and marks the footer `BrandFooter` renders; `samvaBrandPlugin` when omitted. */
193
+ readonly brandPlugin?: BrandPlugin | undefined;
189
194
  }
190
195
  interface ProjectTransform {
191
196
  readonly assets: readonly AssetEntry[];
@@ -196,23 +201,15 @@ interface ProjectTransform {
196
201
  readonly diagnostics: readonly EmailDiagnostic[];
197
202
  }
198
203
  /**
199
- * The `samva:brand` specifier a project imports — from its theme's `@import`s
200
- * or its TSX — so a host can fetch that brand before it compiles. `undefined`
201
- * when the project imports no brand. A project that names two brands gets the
204
+ * The brand specifier a project imports — from its theme's `@import`s or its
205
+ * TSX — so a host can fetch that brand before it compiles. `undefined` when
206
+ * the project imports no brand. A project that names two brands gets the
202
207
  * first here and a refusal from the compile.
203
208
  */
204
- declare const projectBrandSpecifier: (files: Readonly<Record<string, string | Uint8Array>>, themePath?: string) => string | undefined;
209
+ declare const projectBrandSpecifier: (files: Readonly<Record<string, string | Uint8Array>>, themePath?: string, brandPlugin?: BrandPlugin) => string | undefined;
205
210
  /** Read a whole project, then compile the classes its scripts used. */
206
211
  declare const transformProject: (files: Readonly<Record<string, string | Uint8Array>>, options: TransformOptions) => Promise<ProjectTransform>;
207
212
  //#endregion
208
- //#region src/email/theme-imports.d.ts
209
- /** The CSS and TSX specifier a template uses to reach its organization's brand. */
210
- declare const BRAND_SPECIFIER = "samva:brand";
211
- /** The brand a `samva:brand` specifier names: `undefined` slug means the default brand. */
212
- declare const parseBrandSpecifier: (specifier: string) => {
213
- readonly slug: string | undefined;
214
- } | undefined;
215
- //#endregion
216
213
  //#region src/email/brand-footer.d.ts
217
214
  /** A footer block for the send path to append, and its plain-text tail. */
218
215
  interface RenderedBrandFooter {
@@ -223,15 +220,16 @@ interface RenderedBrandFooter {
223
220
  * Render `BrandFooter` against a brand on its own, for the send path to
224
221
  * append to a message whose template renders no footer of its own. It
225
222
  * compiles the same component, styled by the same brand theme, a template
226
- * would get from `samva:brand`. Without `unsubscribe` the unsubscribe row is
223
+ * would get from its brand import. Without `unsubscribe` the unsubscribe row is
227
224
  * left out.
228
225
  */
229
226
  declare const renderBrandFooter: (brand: EmailBrand, options: {
230
227
  readonly unsubscribe: boolean;
228
+ readonly brandPlugin?: BrandPlugin | undefined;
231
229
  }) => Promise<RenderedBrandFooter>;
232
230
  /**
233
- * Render content against a brand as a project whose `theme.css` is
234
- * `@import "samva:brand";` renders it: Tailwind compiles the brand theme for
231
+ * Render content against a brand as a project whose `theme.css` imports only
232
+ * the brand renders it: Tailwind compiles the brand theme for
235
233
  * the classes the content names, `BrandLogo` and `BrandFooter` are bound to the
236
234
  * brand, and the render inlines the result. A brand preview renders through
237
235
  * this, so it shows what a template importing the brand compiles to.
@@ -240,7 +238,7 @@ declare const renderBrandSample: (brand: EmailBrand, sample: {
240
238
  /** Every class the content names; nothing is discovered from source here. */
241
239
  readonly classes: readonly string[];
242
240
  readonly render: (components: BrandComponents) => EmailContent;
243
- }) => Promise<RenderedEmail>;
241
+ }, brandPlugin?: BrandPlugin) => Promise<RenderedEmail>;
244
242
  //#endregion
245
243
  //#region src/sml/lower-project.d.ts
246
244
  interface CompiledTemplate {
@@ -285,12 +283,12 @@ declare const checkTemplateCompatibility: (ir: TemplateIr, fixtures: Readonly<Re
285
283
  * and the entry's path. Imports of files that are absent are trusted to exist, and the brand,
286
284
  * Tailwind and theme are not resolved, so the result holds only findings the source itself owns.
287
285
  */
288
- declare const checkStaticProfile: (files: string | Readonly<Record<string, string | Uint8Array>>, entry?: string) => readonly TemplateDiagnostic[];
286
+ declare const checkStaticProfile: (files: string | Readonly<Record<string, string | Uint8Array>>, entry?: string, brandPlugin?: BrandPlugin) => readonly TemplateDiagnostic[];
289
287
  /**
290
288
  * The errors in `after` that `before` did not already have, compared by code and message so a
291
289
  * finding that only moved does not count as new.
292
290
  */
293
291
  declare const introducedProfileErrors: (before: readonly TemplateDiagnostic[], after: readonly TemplateDiagnostic[]) => readonly TemplateDiagnostic[];
294
292
  //#endregion
295
- export { type Ancestry, type AppliedStyles, BRAND_SPECIFIER, COMPATIBILITY_PROVENANCE, type ClassDiscovery, type CompatibilityInput, type CompatibilityProvenance, type CompileTemplateOptions, type CompiledSelector, type CompiledTemplate, type Compound, type CssDeclaration, type CssRule, EMAIL_CLIENT_MATRIX, ESSENTIAL_CONSTRUCTS, type EmailClientTarget, type EmailContent, type ProjectTransform, type RenderedBrandFooter, type Specificity, type Stylesheet, TAILWIND_VERSION, TEMPLATE_NAME_PATTERN, type TailwindLimits, type TailwindOptions, type TailwindOutput, type TransformOptions, applyStylesheet, checkEmailCompatibility, checkStaticProfile, checkTemplateCompatibility, classListOf, compileSelector, compileTailwind, compileTemplate, discoverClassCandidates, downlevelColors, downlevelMediaQuery, finalizeValue, foldCalc, formatColor, hasBlockingCompatibility, introducedProfileErrors, matchesSelector, parseBrandSpecifier, parseColor, parseStylesheet, projectBrandSpecifier, renderBrandFooter, renderBrandSample, renderHeadCss, resolveVariables, transformProject, unknownCoverage };
293
+ export { type Ancestry, type AppliedStyles, COMPATIBILITY_PROVENANCE, type ClassDiscovery, type CompatibilityInput, type CompatibilityProvenance, type CompileTemplateOptions, type CompiledSelector, type CompiledTemplate, type Compound, type CssDeclaration, type CssRule, EMAIL_CLIENT_MATRIX, ESSENTIAL_CONSTRUCTS, type EmailClientTarget, type EmailContent, type ProjectTransform, type RenderedBrandFooter, type Specificity, type Stylesheet, TAILWIND_VERSION, TEMPLATE_NAME_PATTERN, type TailwindLimits, type TailwindOptions, type TailwindOutput, type TransformOptions, applyStylesheet, checkEmailCompatibility, checkStaticProfile, checkTemplateCompatibility, classListOf, compileSelector, compileTailwind, compileTemplate, discoverClassCandidates, downlevelColors, downlevelMediaQuery, finalizeValue, foldCalc, formatColor, hasBlockingCompatibility, introducedProfileErrors, matchesSelector, parseBrandSpecifier, parseColor, parseStylesheet, projectBrandSpecifier, renderBrandFooter, renderBrandSample, renderHeadCss, resolveVariables, transformProject, unknownCoverage };
296
294
  //# sourceMappingURL=compiler.d.ts.map