@samva/markup 0.12.0 → 0.13.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/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ ## @samva/markup@0.13.1
2
+
3
+ ### The authoring reference omits channel-only upgrade recipes
4
+
5
+ `generated/reference.md` lists upgrade recipes only for diagnostics that apply to every template,
6
+ matching its diagnostics table and `contract.json`. The `whatsapp-template-name` recipe stays in the
7
+ diagnostic codes, where an agent migrating a WhatsApp template reads it.
8
+
9
+ ## @samva/markup@0.13.0
10
+
11
+ ### Meta's WhatsApp template name comes from the template's handle
12
+
13
+ `<WhatsApp>` no longer takes `name`. Samva registers each WhatsApp template with Meta under its
14
+ handle, so an authored name could only disagree with it. A template that still sets `name` fails
15
+ with `whatsapp-template-name`, which carries an upgrade recipe: remove the attribute.
16
+
17
+ `WhatsAppProps`, `IrWhatsApp`, `RenderedIrWhatsApp`, `WhatsAppChannelForm` and the editor host's
18
+ `WhatsAppRender` drop `name`. A `WhatsAppEdit` attribute edit names only `language` or `category`,
19
+ with a string value. The WhatsApp workspace no longer shows a Template name field.
20
+
1
21
  ## @samva/markup@0.11.4
2
22
 
3
23
  ### Diagnostics carry their upgrade recipe
@@ -1,4 +1,4 @@
1
- import { D as samvaBrandPlugin, O as BRAND_FOOTER_MARKER, k as UNSUBSCRIBE_LINK, r as escapeAttribute } from "./html-DjPxwjLi.js";
1
+ import { D as samvaBrandPlugin, O as BRAND_FOOTER_MARKER, k as UNSUBSCRIBE_LINK, r as escapeAttribute } from "./html-eOZfDwVf.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. */
package/dist/brand.js CHANGED
@@ -1,4 +1,4 @@
1
- import { D as samvaBrandPlugin } from "./html-DjPxwjLi.js";
1
+ import { D as samvaBrandPlugin } from "./html-eOZfDwVf.js";
2
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";
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-C45QBKX9.js";
4
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 };
@@ -2,7 +2,7 @@ import { n as parseBrandSpecifier, t as BrandPlugin } from "./brand-plugin-CHhce
2
2
  import { a as TemplateDiagnostic, r as EmailDiagnostic } from "./diagnostics-CUYlQ_6x.js";
3
3
  import { r as EmailNode } from "./jsx-runtime-DC7xG0aA.js";
4
4
  import { n as AssetEntry, p as EmailBrand, u as BrandComponents } from "./assets-DCULM6QD.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-C28ucTh8.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-C6OTH8BQ.js";
6
6
  import { a as TEMPLATE_NAME_PATTERN, n as EmailContent } from "./template-B-UDXK8a.js";
7
7
  import { caniemail } from "caniemail";
8
8
  //#region src/email/compatibility.d.ts
package/dist/compiler.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import { n as isObject } from "./guards-eV1qLbf2.js";
2
2
  import { n as TEMPLATE_NAME_PATTERN } from "./template-zLUjcwfF.js";
3
- import { D as samvaBrandPlugin, T as parseBrandSpecifier, _ as renderIr, w as brandSpecifierPattern } from "./html-DjPxwjLi.js";
3
+ import { D as samvaBrandPlugin, T as parseBrandSpecifier, _ as renderIr, w as brandSpecifierPattern } from "./html-eOZfDwVf.js";
4
4
  import { c as parseBrandCss, d as formatColor, f as parseColor, i as brandTailwindCss, u as downlevelColors } from "./theme-css-CEUrDpqq.js";
5
- import { a as downlevelMediaQuery, c as renderHeadCss, i as matchesSelector, l as resolveVariables, n as applyStylesheet, o as finalizeValue, r as classListOf, s as foldCalc, u as unquote } from "./ir-De6pTH51.js";
6
- import { i as buildLineMap, s as lineColumn } from "./diagnostic-codes-BduqCv_h.js";
7
- import { a as lowerProject, d as resolveFromDirectory, f as resolveThemeImports, i as completed, l as Findings, n as introducedProfileErrors, r as loadProject, t as checkStaticProfile, u as projectDirectory } from "./profile-CWZ2kkt-.js";
8
- import { c as checkAssetBase, i as withoutUnsubscribeRow, l as isAssetPath, n as brandComponents, o as assetEntry, s as assetUrl, t as BRAND_COMPONENT_CLASSES } from "./brand-DTctcDmf.js";
5
+ import { a as downlevelMediaQuery, c as renderHeadCss, i as matchesSelector, l as resolveVariables, n as applyStylesheet, o as finalizeValue, r as classListOf, s as foldCalc, u as unquote } from "./ir-C-ubz6N3.js";
6
+ import { i as buildLineMap, s as lineColumn } from "./diagnostic-codes-DdZ4tka5.js";
7
+ import { a as lowerProject, d as resolveFromDirectory, f as resolveThemeImports, i as completed, l as Findings, n as introducedProfileErrors, r as loadProject, t as checkStaticProfile, u as projectDirectory } from "./profile-CXnkfEMi.js";
8
+ import { c as checkAssetBase, i as withoutUnsubscribeRow, l as isAssetPath, n as brandComponents, o as assetEntry, s as assetUrl, t as BRAND_COMPONENT_CLASSES } from "./brand-C45QBKX9.js";
9
9
  import { t as EmailCompileError } from "./diagnostics-C0Tr8-eQ.js";
10
- import { n as compileEmail, r as renderEmailContent } from "./render-BVW-ESn-.js";
10
+ import { n as compileEmail, r as renderEmailContent } from "./render-Cja448i0.js";
11
11
  import { caniemail, parseHtml, rawData } from "caniemail";
12
12
  import { generate, ident, parse, walk } from "css-tree";
13
13
  import { parse as parse$1 } from "@babel/parser";
@@ -1,4 +1,4 @@
1
- import { i as escapeText, r as escapeAttribute } from "./html-DjPxwjLi.js";
1
+ import { i as escapeText, r as escapeAttribute } from "./html-eOZfDwVf.js";
2
2
  import { a as jsx, c as rawMarkup, l as reportEmailDiagnostic, n as Fragment, s as normalizeChildren, t as EmailNode } from "./jsx-runtime-CBlexoeR.js";
3
3
  //#region src/email/components.ts
4
4
  /** Marks the hidden preview block so the renderer can read it back and the text lane can keep it. */
@@ -385,6 +385,16 @@ const DIAGNOSTIC_CODES = codes({
385
385
  fix: "Write text and bindings, with a newline for a line break.",
386
386
  channelOnly: true
387
387
  },
388
+ "whatsapp-template-name": {
389
+ summary: "A <WhatsApp> names its Meta template; the name comes from the template's handle.",
390
+ fix: "Remove `name` from <WhatsApp>.",
391
+ upgrade: [
392
+ "Remove the `name` attribute from every <WhatsApp> element in the project, templates and partials alike. Samva registers each WhatsApp template with Meta under its handle, with hyphens as underscores, so the attribute has no replacement. Change nothing else.",
393
+ "If an authored name differs from the name the handle produces and the template is already registered with Meta under it, tell the user: keeping that Meta name means renaming the template's handle, which you do not do yourself.",
394
+ "Compile each entry and render every declared fixture to confirm the WhatsApp output is otherwise unchanged. Resolve errors before requesting one saved version for the whole project, approved by the user; do not publish."
395
+ ].join("\n\n"),
396
+ channelOnly: true
397
+ },
388
398
  "whatsapp-structure": {
389
399
  summary: "WhatsApp content sits outside Header, Body, Footer or Buttons.",
390
400
  fix: "Move it into <WhatsApp.Body>.",
@@ -354,6 +354,12 @@ declare const DIAGNOSTIC_CODES: {
354
354
  readonly fix: "Write text and bindings, with a newline for a line break.";
355
355
  readonly channelOnly: true;
356
356
  };
357
+ readonly "whatsapp-template-name": {
358
+ readonly summary: "A <WhatsApp> names its Meta template; the name comes from the template's handle.";
359
+ readonly fix: "Remove `name` from <WhatsApp>.";
360
+ readonly upgrade: string;
361
+ readonly channelOnly: true;
362
+ };
357
363
  readonly "whatsapp-structure": {
358
364
  readonly summary: "WhatsApp content sits outside Header, Body, Footer or Buttons.";
359
365
  readonly fix: "Move it into <WhatsApp.Body>.";
@@ -1,3 +1,3 @@
1
- import { a as createDiagnosticFormatter, i as buildLineMap, n as DIAGNOSTIC_CODE_FAMILIES, o as formatDiagnostic, r as hasUpgradeRecipe, s as lineColumn, t as DIAGNOSTIC_CODES } from "./diagnostic-codes-BduqCv_h.js";
1
+ import { a as createDiagnosticFormatter, i as buildLineMap, n as DIAGNOSTIC_CODE_FAMILIES, o as formatDiagnostic, r as hasUpgradeRecipe, s as lineColumn, t as DIAGNOSTIC_CODES } from "./diagnostic-codes-DdZ4tka5.js";
2
2
  import { n as formatEmailDiagnostic, r as hasBlockingDiagnostic, t as EmailCompileError } from "./diagnostics-C0Tr8-eQ.js";
3
3
  export { DIAGNOSTIC_CODES, DIAGNOSTIC_CODE_FAMILIES, EmailCompileError, buildLineMap, createDiagnosticFormatter, formatDiagnostic, formatEmailDiagnostic, hasBlockingDiagnostic, hasUpgradeRecipe, lineColumn };
package/dist/edit.d.ts CHANGED
@@ -161,7 +161,6 @@ type WhatsAppButtonRow = {
161
161
  readonly summary: string;
162
162
  };
163
163
  interface WhatsAppChannelForm {
164
- readonly name: AttributeState;
165
164
  readonly language: AttributeState;
166
165
  readonly category: AttributeState;
167
166
  /** Null when the template has no header. */
@@ -191,8 +190,8 @@ type SmsEdit = {
191
190
  type WhatsAppPart = "header" | "body" | "footer";
192
191
  type WhatsAppEdit = {
193
192
  readonly kind: "attribute";
194
- readonly name: "name" | "language" | "category";
195
- readonly value: string | undefined;
193
+ readonly name: "language" | "category";
194
+ readonly value: string;
196
195
  } | {
197
196
  readonly kind: "headerType";
198
197
  readonly value: WhatsAppHeaderType;
package/dist/edit.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { n as isObject } from "./guards-eV1qLbf2.js";
2
2
  import { d as completeJsxSourceLocation, u as JSX_SOURCE_LOCATION_PROP } from "./jsx-runtime-CBlexoeR.js";
3
- import { i as buildLineMap } from "./diagnostic-codes-BduqCv_h.js";
4
- import { c as WHATSAPP_HEADER_TYPES, n as introducedProfileErrors, o as SMS_CATEGORIES, s as WHATSAPP_CATEGORIES, t as checkStaticProfile } from "./profile-CWZ2kkt-.js";
3
+ import { i as buildLineMap } from "./diagnostic-codes-DdZ4tka5.js";
4
+ import { c as WHATSAPP_HEADER_TYPES, n as introducedProfileErrors, o as SMS_CATEGORIES, s as WHATSAPP_CATEGORIES, t as checkStaticProfile } from "./profile-CXnkfEMi.js";
5
5
  import { parse } from "@babel/parser";
6
6
  //#region src/source-edit.ts
7
7
  const node = (value) => isObject(value) && "type" in value && typeof value.type === "string" && "start" in value && typeof value.start === "number" && "end" in value && typeof value.end === "number";
@@ -806,7 +806,6 @@ const readWhatsAppChannel = (source) => {
806
806
  if (channel === null || channel.body === null) return null;
807
807
  const { root } = channel;
808
808
  return {
809
- name: attributeState(source, root, "name"),
810
809
  language: attributeState(source, root, "language"),
811
810
  category: attributeState(source, root, "category"),
812
811
  header: channel.header === null ? null : segmentsOf(source, channel.header),
@@ -856,14 +855,12 @@ const editWhatsAppChannel = (source, edit) => {
856
855
  };
857
856
  const { root, local } = channel;
858
857
  switch (edit.kind) {
859
- case "attribute": {
860
- if (edit.name === "category" && !WHATSAPP_CATEGORIES.includes(edit.value ?? "")) return {
858
+ case "attribute":
859
+ if (edit.name === "category" && !WHATSAPP_CATEGORIES.includes(edit.value)) return {
861
860
  ok: false,
862
861
  reason: "Choose a WhatsApp category."
863
862
  };
864
- const value = edit.name === "name" && edit.value === "" ? void 0 : edit.value ?? "";
865
- return setAttribute(source, root, edit.name, value);
866
- }
863
+ return setAttribute(source, root, edit.name, edit.value);
867
864
  case "headerType":
868
865
  if (channel.header === null) return {
869
866
  ok: false,
package/dist/email.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as Email, c as PREHEADER_ATTRIBUTE, d as equalColumnWidth, f as preheaderNode, i as Divider, l as Section, n as Column, o as Image, r as Columns, s as Link, t as Button, u as Spacer } from "./components-Bl6DT21n.js";
1
+ import { a as Email, c as PREHEADER_ATTRIBUTE, d as equalColumnWidth, f as preheaderNode, i as Divider, l as Section, n as Column, o as Image, r as Columns, s as Link, t as Button, u as Spacer } from "./components-DycSEKva.js";
2
2
  export { Button, Column, Columns, Divider, Email, Image, Link, PREHEADER_ATTRIBUTE, Section, Spacer, equalColumnWidth, preheaderNode };
@@ -1157,7 +1157,6 @@ const renderIrWhatsApp = (ir, input, options = {}) => {
1157
1157
  resolveButtons(ctx, whatsapp.buttons ?? [], buttons, 0);
1158
1158
  settle(ctx);
1159
1159
  return {
1160
- ...whatsapp.name === void 0 ? {} : { name: whatsapp.name },
1161
1160
  language: whatsapp.language,
1162
1161
  category: whatsapp.category,
1163
1162
  ...header === void 0 ? {} : { header },
@@ -1,5 +1,5 @@
1
1
  import { n as isObject } from "./guards-eV1qLbf2.js";
2
- import { a as kebab, n as declarationValue, r as escapeAttribute } from "./html-DjPxwjLi.js";
2
+ import { a as kebab, n as declarationValue, r as escapeAttribute } from "./html-eOZfDwVf.js";
3
3
  import { t as EmailNode } from "./jsx-runtime-CBlexoeR.js";
4
4
  //#region src/email/font-stacks.ts
5
5
  /** Generic families every email client understands as the last resort in a stack. */
@@ -1,13 +1,13 @@
1
1
  import { n as isObject } from "./guards-eV1qLbf2.js";
2
2
  import { n as inputSchema, r as jsonSchema } from "./input-schema-zjkgrXn2.js";
3
3
  import { n as TEMPLATE_NAME_PATTERN, t as TEMPLATE_ID_PATTERN } from "./template-zLUjcwfF.js";
4
- import { D as samvaBrandPlugin, E as reservedBrandAttributes, O as BRAND_FOOTER_MARKER, S as emailElement, T as parseBrandSpecifier, b as EMAIL_ELEMENTS, s as serializeStyle, t as checkAttribute } from "./html-DjPxwjLi.js";
4
+ import { D as samvaBrandPlugin, E as reservedBrandAttributes, O as BRAND_FOOTER_MARKER, S as emailElement, T as parseBrandSpecifier, b as EMAIL_ELEMENTS, s as serializeStyle, t as checkAttribute } from "./html-eOZfDwVf.js";
5
5
  import { FORMATTER_NAMES } from "./fmt.js";
6
6
  import { i as drainEmailDiagnostics, t as EmailNode } from "./jsx-runtime-CBlexoeR.js";
7
- import { a as Email, d as equalColumnWidth, i as Divider, l as Section, n as Column, o as Image, r as Columns, s as Link, t as Button, u as Spacer } from "./components-Bl6DT21n.js";
8
- import { n as applyStylesheet } from "./ir-De6pTH51.js";
9
- import { i as buildLineMap, s as lineColumn, t as DIAGNOSTIC_CODES } from "./diagnostic-codes-BduqCv_h.js";
10
- import { l as isAssetPath, n as brandComponents } from "./brand-DTctcDmf.js";
7
+ import { a as Email, d as equalColumnWidth, i as Divider, l as Section, n as Column, o as Image, r as Columns, s as Link, t as Button, u as Spacer } from "./components-DycSEKva.js";
8
+ import { n as applyStylesheet } from "./ir-C-ubz6N3.js";
9
+ import { i as buildLineMap, s as lineColumn, t as DIAGNOSTIC_CODES } from "./diagnostic-codes-DdZ4tka5.js";
10
+ import { l as isAssetPath, n as brandComponents } from "./brand-C45QBKX9.js";
11
11
  import { parse } from "css-tree";
12
12
  import { parse as parse$1 } from "@babel/parser";
13
13
  //#region src/email/theme-imports.ts
@@ -284,11 +284,7 @@ const WHATSAPP_HEADER_TYPES = [
284
284
  /** The attributes `<Sms>` accepts. */
285
285
  const SMS_ATTRIBUTES = ["category"];
286
286
  /** The attributes `<WhatsApp>` accepts. */
287
- const WHATSAPP_ATTRIBUTES = [
288
- "name",
289
- "language",
290
- "category"
291
- ];
287
+ const WHATSAPP_ATTRIBUTES = ["language", "category"];
292
288
  //#endregion
293
289
  //#region src/whatsapp-template.ts
294
290
  const isConcat = (value) => typeof value === "object" && value !== null && "concat" in value;
@@ -2545,8 +2541,13 @@ const readRoot = (input, field, imported, specifier) => {
2545
2541
  local: tag
2546
2542
  };
2547
2543
  };
2548
- /** The string-literal attributes of a channel root; anything else is reported. */
2549
- const readAttributes = (input, root, allowed) => {
2544
+ /** Meta's template name is not authored: Samva registers the template under its handle. */
2545
+ const WHATSAPP_REFUSED = { name: {
2546
+ code: "whatsapp-template-name",
2547
+ message: "Meta's name for a WhatsApp template comes from the template's handle, not from <WhatsApp>.",
2548
+ fix: "Remove `name`; rename the template's handle to change its Meta name."
2549
+ } };
2550
+ const readAttributes = (input, root, allowed, refused = {}) => {
2550
2551
  const values = /* @__PURE__ */ new Map();
2551
2552
  for (const attribute of attributesOf(root.node)) {
2552
2553
  if (attribute.type === "JSXSpreadAttribute") {
@@ -2556,6 +2557,11 @@ const readAttributes = (input, root, allowed) => {
2556
2557
  const nameNode = child(attribute, "name");
2557
2558
  const name = nameNode === void 0 ? "" : jsxName(nameNode);
2558
2559
  if (name === "key") continue;
2560
+ const refusal = Object.hasOwn(refused, name) ? refused[name] : void 0;
2561
+ if (refusal !== void 0) {
2562
+ input.lowerer.error(root.ctx, attribute, refusal.code, refusal.message, refusal.fix);
2563
+ continue;
2564
+ }
2559
2565
  if (!allowed.includes(name)) {
2560
2566
  input.lowerer.error(root.ctx, attribute, "unknown-attribute", `\`${name}\` is not an attribute of this component.`, `The attributes are ${allowed.join(", ")}.`);
2561
2567
  continue;
@@ -2658,7 +2664,7 @@ const whatsappChannel = (input, field) => {
2658
2664
  if (root === void 0) return void 0;
2659
2665
  const { lowerer } = input;
2660
2666
  const { ctx } = root;
2661
- const attributes = readAttributes(input, root, WHATSAPP_ATTRIBUTES);
2667
+ const attributes = readAttributes(input, root, WHATSAPP_ATTRIBUTES, WHATSAPP_REFUSED);
2662
2668
  for (const required of ["category", "language"]) if (!attributes.has(required)) lowerer.error(ctx, root.node, "missing-attribute", `<WhatsApp> needs \`${required}\`.`, required === "category" ? `Write category="utility" (${WHATSAPP_CATEGORIES.join(", ")}).` : "Write language=\"en_US\".");
2663
2669
  const category = attributes.get("category");
2664
2670
  const categoryNode = attributesOf(root.node).find((attribute) => text(child(attribute, "name") ?? attribute, "name") === "category");
@@ -2749,9 +2755,7 @@ const whatsappChannel = (input, field) => {
2749
2755
  }
2750
2756
  if (input.schema !== void 0) input.checks.push(...checkBindings(input.schema, roots, []));
2751
2757
  if (category === void 0 || !attributes.has("language")) return void 0;
2752
- const name = attributes.get("name");
2753
2758
  return {
2754
- ...name === void 0 ? {} : { name },
2755
2759
  category,
2756
2760
  language: attributes.get("language"),
2757
2761
  ...header === void 0 ? {} : { header },
@@ -176,7 +176,6 @@ type IrWhatsAppButtonNode = IrWhatsAppButton | {
176
176
  readonly src?: IrSource;
177
177
  };
178
178
  interface IrWhatsApp {
179
- readonly name?: string;
180
179
  readonly category: string;
181
180
  readonly language: string;
182
181
  /** For a media header, `content` is the media URL. */
@@ -247,7 +246,6 @@ type RenderedWhatsAppButton = {
247
246
  };
248
247
  /** A WhatsApp message with every text rendered and every conditional button resolved. */
249
248
  interface RenderedIrWhatsApp {
250
- readonly name?: string;
251
249
  readonly language: string;
252
250
  readonly category: string;
253
251
  /** For a media header, `text` is the media URL. */
@@ -1,7 +1,7 @@
1
- import { S as emailElement, o as serializeEmailHtml } from "./html-DjPxwjLi.js";
1
+ import { S as emailElement, o as serializeEmailHtml } from "./html-eOZfDwVf.js";
2
2
  import { i as drainEmailDiagnostics, t as EmailNode } from "./jsx-runtime-CBlexoeR.js";
3
- import { f as preheaderNode } from "./components-Bl6DT21n.js";
4
- import { n as applyStylesheet } from "./ir-De6pTH51.js";
3
+ import { f as preheaderNode } from "./components-DycSEKva.js";
4
+ import { n as applyStylesheet } from "./ir-C-ubz6N3.js";
5
5
  import { r as hasBlockingDiagnostic, t as EmailCompileError } from "./diagnostics-C0Tr8-eQ.js";
6
6
  //#region src/email/text.ts
7
7
  const PARAGRAPH = "\n\n";
package/dist/render.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { C as isUrlAttribute, S as emailElement, _ as EmailGlobalProps, b as EmailStyle, g as EmailElementSpec, h as EmailAttributeSpec, m as EMAIL_GLOBAL_ATTRIBUTES, p as EMAIL_ELEMENTS, r as EmailNode, v as EmailIntrinsicElements, x as EmailTag, y as EmailLayout } from "./jsx-runtime-DC7xG0aA.js";
2
- import { $ as IrWhatsApp, A as emailAuthoringTarget, B as IrElement, D as EmittedPosition, E as EmailElementSelection, F as emailSelections, G as IrPredicate, H as IrFormatter, I as renderIrPreview, J as IrRawPart, K as IrPreheader, L as IrArithmetic, M as emailSelectionAncestry, N as emailSelectionIndex, O as INSTANCE_PATH_ATTRIBUTE, P as emailSelectionSiblings, Q as IrValue, R as IrComparison, U as IrIf, V as IrEmail, W as IrNode, X as IrSource, Y as IrSms, Z as IrStyle, a as renderEmailContent, at as RenderedPosition, c as checkAttribute, ct as TemplateIr, d as escapeText, et as IrWhatsAppButton, f as kebab, i as compileEmail, it as RenderedIrWhatsApp, j as emailDefinitionInstances, k as IrPreview, l as declarationValue, m as serializeStyle, n as EmailCompileOutput, nt as RenderOptions, o as EmailHtmlOutput, ot as RenderedWhatsAppButton, p as serializeEmailHtml, q as IrRaw, r as RenderedEmail, rt as RenderedIrEmail, s as SerializeEmailOptions, st as SML_IR_VERSION, t as EMAIL_DOCTYPE, tt as IrWhatsAppButtonNode, u as escapeAttribute, z as IrEach } from "./render-C28ucTh8.js";
2
+ import { $ as IrWhatsApp, A as emailAuthoringTarget, B as IrElement, D as EmittedPosition, E as EmailElementSelection, F as emailSelections, G as IrPredicate, H as IrFormatter, I as renderIrPreview, J as IrRawPart, K as IrPreheader, L as IrArithmetic, M as emailSelectionAncestry, N as emailSelectionIndex, O as INSTANCE_PATH_ATTRIBUTE, P as emailSelectionSiblings, Q as IrValue, R as IrComparison, U as IrIf, V as IrEmail, W as IrNode, X as IrSource, Y as IrSms, Z as IrStyle, a as renderEmailContent, at as RenderedPosition, c as checkAttribute, ct as TemplateIr, d as escapeText, et as IrWhatsAppButton, f as kebab, i as compileEmail, it as RenderedIrWhatsApp, j as emailDefinitionInstances, k as IrPreview, l as declarationValue, m as serializeStyle, n as EmailCompileOutput, nt as RenderOptions, o as EmailHtmlOutput, ot as RenderedWhatsAppButton, p as serializeEmailHtml, q as IrRaw, r as RenderedEmail, rt as RenderedIrEmail, s as SerializeEmailOptions, st as SML_IR_VERSION, t as EMAIL_DOCTYPE, tt as IrWhatsAppButtonNode, u as escapeAttribute, z as IrEach } from "./render-C6OTH8BQ.js";
3
3
  //#region src/render-ir.d.ts
4
4
  interface IrRenderDiagnostic {
5
5
  readonly code: string;
package/dist/render.js CHANGED
@@ -1,6 +1,6 @@
1
- import { C as isUrlAttribute, S as emailElement, _ as renderIr, a as kebab, b as EMAIL_ELEMENTS, c as INSTANCE_PATH_ATTRIBUTE, d as emailSelectionAncestry, f as emailSelectionIndex, g as IrRenderError, h as renderIrPreview, i as escapeText, l as emailAuthoringTarget, m as emailSelections, n as declarationValue, o as serializeEmailHtml, p as emailSelectionSiblings, r as escapeAttribute, s as serializeStyle, t as checkAttribute, u as emailDefinitionInstances, v as renderIrSms, x as EMAIL_GLOBAL_ATTRIBUTES, y as renderIrWhatsApp } from "./html-DjPxwjLi.js";
2
- import { t as SML_IR_VERSION } from "./ir-De6pTH51.js";
3
- import { i as deriveEmailText, n as compileEmail, r as renderEmailContent, t as EMAIL_DOCTYPE } from "./render-BVW-ESn-.js";
1
+ import { C as isUrlAttribute, S as emailElement, _ as renderIr, a as kebab, b as EMAIL_ELEMENTS, c as INSTANCE_PATH_ATTRIBUTE, d as emailSelectionAncestry, f as emailSelectionIndex, g as IrRenderError, h as renderIrPreview, i as escapeText, l as emailAuthoringTarget, m as emailSelections, n as declarationValue, o as serializeEmailHtml, p as emailSelectionSiblings, r as escapeAttribute, s as serializeStyle, t as checkAttribute, u as emailDefinitionInstances, v as renderIrSms, x as EMAIL_GLOBAL_ATTRIBUTES, y as renderIrWhatsApp } from "./html-eOZfDwVf.js";
2
+ import { t as SML_IR_VERSION } from "./ir-C-ubz6N3.js";
3
+ import { i as deriveEmailText, n as compileEmail, r as renderEmailContent, t as EMAIL_DOCTYPE } from "./render-Cja448i0.js";
4
4
  //#region src/sms-segments.ts
5
5
  const GSM7_EXTENDED = /* @__PURE__ */ new Set([
6
6
  "[",
@@ -2,7 +2,6 @@ import { r as EmailNode, t as EmailChild } from "./jsx-runtime-DC7xG0aA.js";
2
2
  import { n as WhatsAppCategory, r as WhatsAppHeaderType } from "./channel-specs-C2feJwuP.js";
3
3
  //#region src/whatsapp.d.ts
4
4
  interface WhatsAppProps {
5
- readonly name?: string | undefined;
6
5
  /** The Meta language code, for example `en_US`. */
7
6
  readonly language: string;
8
7
  readonly category: WhatsAppCategory;
@@ -575,11 +575,6 @@
575
575
  "name": "WhatsApp",
576
576
  "import": "@samva/markup/whatsapp",
577
577
  "props": [
578
- {
579
- "name": "name",
580
- "type": "string",
581
- "optional": true
582
- },
583
578
  {
584
579
  "name": "language",
585
580
  "type": "string",
@@ -1292,7 +1287,7 @@
1292
1287
  },
1293
1288
  "whatsapp": {
1294
1289
  "categories": ["marketing", "utility", "authentication"],
1295
- "attributes": ["name", "language", "category"],
1290
+ "attributes": ["language", "category"],
1296
1291
  "headerTypes": ["text", "image", "video", "document"]
1297
1292
  }
1298
1293
  },
@@ -1765,6 +1760,13 @@
1765
1760
  "fix": "Move it into <WhatsApp.Body>.",
1766
1761
  "channelOnly": true
1767
1762
  },
1763
+ {
1764
+ "code": "whatsapp-template-name",
1765
+ "summary": "A <WhatsApp> names its Meta template; the name comes from the template's handle.",
1766
+ "fix": "Remove `name` from <WhatsApp>.",
1767
+ "upgrade": "Remove the `name` attribute from every <WhatsApp> element in the project, templates and partials alike. Samva registers each WhatsApp template with Meta under its handle, with hyphens as underscores, so the attribute has no replacement. Change nothing else.\n\nIf an authored name differs from the name the handle produces and the template is already registered with Meta under it, tell the user: keeping that Meta name means renaming the template's handle, which you do not do yourself.\n\nCompile each entry and render every declared fixture to confirm the WhatsApp output is otherwise unchanged. Resolve errors before requesting one saved version for the whole project, approved by the user; do not publish.",
1768
+ "channelOnly": true
1769
+ },
1768
1770
  {
1769
1771
  "code": "wrong-channel",
1770
1772
  "summary": "A channel body does not return its own channel component.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@samva/markup",
3
- "version": "0.12.0",
3
+ "version": "0.13.1",
4
4
  "description": "Type-safe authoring and compilation primitives for Samva templates",
5
5
  "keywords": [
6
6
  "email",