@samva/markup 0.9.0 → 0.11.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.
Files changed (92) hide show
  1. package/AGENTS.md +53 -41
  2. package/CHANGELOG.md +379 -0
  3. package/README.md +6 -6
  4. package/dist/assets-B0yseju_.d.ts +115 -0
  5. package/dist/brand-DTctcDmf.js +202 -0
  6. package/dist/brand-plugin-CHhcei-j.d.ts +32 -0
  7. package/dist/{theme-css.d.ts → brand.d.ts} +15 -4
  8. package/dist/brand.js +4 -0
  9. package/dist/compiler.d.ts +236 -18
  10. package/dist/compiler.js +1315 -13
  11. package/dist/{email/components.js → components-Bl6DT21n.js} +4 -4
  12. package/dist/diagnostic-model-DJtqbETd.js +38 -0
  13. package/dist/diagnostic-model-DxthUZBT.d.ts +31 -0
  14. package/dist/{email/diagnostics.js → diagnostics-CnKFefKz.js} +2 -2
  15. package/dist/diagnostics.d.ts +406 -27
  16. package/dist/diagnostics.js +402 -34
  17. package/dist/edit.d.ts +234 -0
  18. package/dist/{channel-edit.js → edit.js} +341 -3
  19. package/dist/email/jsx-dev-runtime.d.ts +1 -1
  20. package/dist/email/jsx-dev-runtime.js +1 -2
  21. package/dist/email/jsx-runtime.d.ts +1 -1
  22. package/dist/email/jsx-runtime.js +1 -85
  23. package/dist/{email/components.d.ts → email.d.ts} +2 -2
  24. package/dist/email.js +2 -0
  25. package/dist/html-DjPxwjLi.js +1637 -0
  26. package/dist/index.d.ts +1 -1
  27. package/dist/index.js +1 -1
  28. package/dist/{cascade-t6kxlgEI.js → ir-De6pTH51.js} +8 -4
  29. package/dist/jsx-runtime-CBlexoeR.js +98 -0
  30. package/dist/{elements-cqldpaJt.d.ts → jsx-runtime-ylIOnaSf.d.ts} +74 -74
  31. package/dist/{profile-BH-OtdDA.js → profile-PUfi3GPH.js} +166 -32
  32. package/dist/{email/render.js → render-D2ht57ZZ.js} +103 -8
  33. package/dist/render-DQKz4Aco.d.ts +548 -0
  34. package/dist/render.d.ts +35 -0
  35. package/dist/render.js +49 -0
  36. package/dist/sms.d.ts +1 -1
  37. package/dist/sms.js +1 -1
  38. package/dist/{template-_QE_rQND.d.ts → template-DMPwxMSq.d.ts} +2 -2
  39. package/dist/{template.js → template-zLUjcwfF.js} +2 -2
  40. package/dist/{theme-css-hFqcPSar.js → theme-css-CEUrDpqq.js} +3 -3
  41. package/dist/whatsapp.d.ts +1 -1
  42. package/dist/whatsapp.js +1 -1
  43. package/generated/contract.json +18 -12
  44. package/generated/reference.md +19 -12
  45. package/package.json +39 -122
  46. package/samva-brand.d.ts +4 -4
  47. package/skills/sml/SKILL.md +2 -2
  48. package/dist/assets-C5duwTya.d.ts +0 -40
  49. package/dist/brand-B-0bN_Ir.d.ts +0 -88
  50. package/dist/channel-edit.d.ts +0 -121
  51. package/dist/channel-specs-B6M_HlaV.js +0 -29
  52. package/dist/compatibility-DbISoFIq.js +0 -589
  53. package/dist/css-BVQKICKC.d.ts +0 -109
  54. package/dist/diagnostic-codes.d.ts +0 -403
  55. package/dist/diagnostic-codes.js +0 -399
  56. package/dist/email/assets.d.ts +0 -2
  57. package/dist/email/assets.js +0 -63
  58. package/dist/email/brand-footer.d.ts +0 -34
  59. package/dist/email/brand-footer.js +0 -63
  60. package/dist/email/brand.d.ts +0 -2
  61. package/dist/email/brand.js +0 -148
  62. package/dist/email/diagnostics.d.ts +0 -2
  63. package/dist/email/elements.d.ts +0 -2
  64. package/dist/email/elements.js +0 -299
  65. package/dist/email/html.d.ts +0 -2
  66. package/dist/email/html.js +0 -321
  67. package/dist/email/render.d.ts +0 -40
  68. package/dist/email/text.d.ts +0 -7
  69. package/dist/email/text.js +0 -100
  70. package/dist/html-BJepOwZ4.d.ts +0 -56
  71. package/dist/ir-aaqNE987.d.ts +0 -252
  72. package/dist/ir.d.ts +0 -2
  73. package/dist/ir.js +0 -7
  74. package/dist/preview-Ekdg1dXG.d.ts +0 -100
  75. package/dist/preview.d.ts +0 -2
  76. package/dist/preview.js +0 -160
  77. package/dist/render-ir.d.ts +0 -19
  78. package/dist/render-ir.js +0 -826
  79. package/dist/sms-segments.d.ts +0 -13
  80. package/dist/sms-segments.js +0 -46
  81. package/dist/source-edit.d.ts +0 -116
  82. package/dist/source-edit.js +0 -343
  83. package/dist/source-locations.d.ts +0 -2
  84. package/dist/source-locations.js +0 -16
  85. package/dist/static.d.ts +0 -57
  86. package/dist/static.js +0 -69
  87. package/dist/tailwind-CMYpZf1v.js +0 -614
  88. package/dist/template.d.ts +0 -2
  89. package/dist/theme-css.js +0 -2
  90. package/dist/theme-imports-lvBw3AD1.js +0 -109
  91. package/dist/transform-Ct2OMkFN.d.ts +0 -153
  92. package/dist/unsubscribe-link-CoesnQDU.js +0 -26
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { i as TEMPLATE_ID_PATTERN, o as TemplateInput, r as SmlTemplate, s as defineTemplate, t as EmailChannel } from "./template-_QE_rQND.js";
1
+ import { i as TEMPLATE_ID_PATTERN, o as TemplateInput, r as SmlTemplate, s as defineTemplate, t as EmailChannel } from "./template-DMPwxMSq.js";
2
2
  export { type EmailChannel, type SmlTemplate, TEMPLATE_ID_PATTERN, type TemplateInput, defineTemplate };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { TEMPLATE_ID_PATTERN, defineTemplate } from "./template.js";
1
+ import { r as defineTemplate, t as TEMPLATE_ID_PATTERN } from "./template-zLUjcwfF.js";
2
2
  export { TEMPLATE_ID_PATTERN, defineTemplate };
@@ -1,6 +1,6 @@
1
1
  import { n as isObject } from "./guards-eV1qLbf2.js";
2
- import { EmailNode } from "./email/jsx-runtime.js";
3
- import { declarationValue, escapeAttribute, kebab } from "./email/html.js";
2
+ import { a as kebab, n as declarationValue, r as escapeAttribute } from "./html-DjPxwjLi.js";
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. */
6
6
  const GENERIC_FAMILIES = /* @__PURE__ */ new Set([
@@ -574,6 +574,10 @@ const applyStylesheet = (root, sheet, options = {}) => {
574
574
  };
575
575
  };
576
576
  //#endregion
577
- export { finalizeValue as a, resolveVariables as c, downlevelMediaQuery as i, unquote as l, classListOf as n, foldCalc as o, matchesSelector as r, renderHeadCss as s, applyStylesheet as t };
577
+ //#region src/ir.ts
578
+ /** The IR version this package writes. A publication pins the version it was built with. */
579
+ const SML_IR_VERSION = 1;
580
+ //#endregion
581
+ export { downlevelMediaQuery as a, renderHeadCss as c, matchesSelector as i, resolveVariables as l, applyStylesheet as n, finalizeValue as o, classListOf as r, foldCalc as s, SML_IR_VERSION as t, unquote as u };
578
582
 
579
- //# sourceMappingURL=cascade-t6kxlgEI.js.map
583
+ //# sourceMappingURL=ir-De6pTH51.js.map
@@ -0,0 +1,98 @@
1
+ import { t as hasProperty } from "./guards-eV1qLbf2.js";
2
+ //#region src/source-locations.ts
3
+ /** The JSX prop that carries a {@link JsxSource} to the email JSX runtime. */
4
+ const JSX_SOURCE_LOCATION_PROP = "__samva_source_location";
5
+ /** Normalize the optional payload a JSX transform attaches. */
6
+ const completeJsxSourceLocation = (source) => {
7
+ if (source?.fileName === void 0 || source.lineNumber === void 0 || source.columnNumber === void 0) return;
8
+ return Object.freeze({
9
+ fileName: source.fileName,
10
+ lineNumber: source.lineNumber,
11
+ columnNumber: source.columnNumber
12
+ });
13
+ };
14
+ //#endregion
15
+ //#region src/email/jsx-runtime.ts
16
+ /** Constructors for the authoring tree. The tree is data; nothing here validates it. */
17
+ const EmailNode = {
18
+ Text: (fields) => ({
19
+ type: "Text",
20
+ ...fields
21
+ }),
22
+ Element: (fields) => ({
23
+ type: "Element",
24
+ ...fields
25
+ }),
26
+ Fragment: (fields) => ({
27
+ type: "Fragment",
28
+ ...fields
29
+ }),
30
+ Raw: (fields) => ({
31
+ type: "Raw",
32
+ ...fields
33
+ })
34
+ };
35
+ const EMAIL_NODE_TYPES = /* @__PURE__ */ new Set([
36
+ "Text",
37
+ "Element",
38
+ "Fragment",
39
+ "Raw"
40
+ ]);
41
+ /** True for a node this runtime built; anything else is not a child it can place. */
42
+ const isEmailNode = (input) => hasProperty(input, "type") && typeof input.type === "string" && EMAIL_NODE_TYPES.has(input.type);
43
+ const Fragment = Symbol.for("@samva/markup/email/Fragment");
44
+ const callSites = [];
45
+ /** Call sites enclosing the element being built, innermost first. */
46
+ const currentEmailOrigins = () => [...callSites].reverse();
47
+ const reported = [];
48
+ /** Record a finding from inside a compiler primitive, located at the authoring call site. */
49
+ const reportEmailDiagnostic = (diagnostic) => {
50
+ reported.push({
51
+ ...diagnostic,
52
+ origins: currentEmailOrigins()
53
+ });
54
+ };
55
+ /** Take and clear the findings primitives reported during a render. */
56
+ const drainEmailDiagnostics = () => reported.splice(0);
57
+ /** Normalize actual values without interpolation or recording proxies. */
58
+ const normalizeChildren = (input) => {
59
+ if (input === null || input === void 0 || typeof input === "boolean") return [];
60
+ if (typeof input === "string") return [EmailNode.Text({ value: input })];
61
+ if (typeof input === "number" && Number.isFinite(input)) return [EmailNode.Text({ value: String(input) })];
62
+ if (Array.isArray(input)) return input.flatMap(normalizeChildren);
63
+ if (isEmailNode(input)) return [input];
64
+ throw new TypeError("Email children must be synchronous text, finite numbers, elements, or arrays");
65
+ };
66
+ /** Compatibility markup emitted by a compiler primitive, with the text it stands for. */
67
+ const rawMarkup = (html, text = "") => EmailNode.Raw({
68
+ html,
69
+ text,
70
+ origins: currentEmailOrigins()
71
+ });
72
+ /** Automatic JSX factory for ordinary HTML and synchronous imported components. */
73
+ const jsx = (type, props) => {
74
+ const { children, [JSX_SOURCE_LOCATION_PROP]: source, ...attributes } = props ?? {};
75
+ const origin = completeJsxSourceLocation(source);
76
+ if (type === Fragment) return EmailNode.Fragment({ children: normalizeChildren(children) });
77
+ if (typeof type === "string") return EmailNode.Element({
78
+ tag: type,
79
+ props: attributes,
80
+ children: normalizeChildren(children),
81
+ origins: origin === void 0 ? currentEmailOrigins() : [origin, ...currentEmailOrigins()],
82
+ authored: origin !== void 0
83
+ });
84
+ if (origin !== void 0) callSites.push(origin);
85
+ try {
86
+ return EmailNode.Fragment({ children: normalizeChildren(type({
87
+ ...attributes,
88
+ children
89
+ })) });
90
+ } finally {
91
+ if (origin !== void 0) callSites.pop();
92
+ }
93
+ };
94
+ const jsxs = jsx;
95
+ //#endregion
96
+ export { jsx as a, rawMarkup as c, completeJsxSourceLocation as d, drainEmailDiagnostics as i, reportEmailDiagnostic as l, Fragment as n, jsxs as o, currentEmailOrigins as r, normalizeChildren as s, EmailNode as t, JSX_SOURCE_LOCATION_PROP as u };
97
+
98
+ //# sourceMappingURL=jsx-runtime-CBlexoeR.js.map
@@ -1,77 +1,5 @@
1
1
  import { r as JsxSourceLocation } from "./source-locations-C8wCsiAy.js";
2
2
  import { r as EmailDiagnostic } from "./diagnostics-B-Gym34R.js";
3
- //#region src/email/jsx-runtime.d.ts
4
- /** Derived authoring tree; source stays canonical. Origins run from inner element to component call sites. */
5
- type EmailNode = {
6
- readonly type: "Text";
7
- readonly value: string;
8
- } | {
9
- readonly type: "Element";
10
- readonly tag: string;
11
- readonly props: Readonly<Record<string, unknown>>;
12
- readonly children: readonly EmailNode[];
13
- readonly origins: readonly JsxSourceLocation[];
14
- /**
15
- * True when this element is written at `origins[0]`; false when a compiler
16
- * primitive generated it and `origins[0]` is the call site that asked for
17
- * it. Both point at the same authoring target, but only an authored element
18
- * can have its own props edited there.
19
- */
20
- readonly authored: boolean;
21
- } | {
22
- readonly type: "Fragment";
23
- readonly children: readonly EmailNode[];
24
- } |
25
- /**
26
- * Compatibility markup a compiler primitive emits verbatim — conditional
27
- * comments and VML. Only the primitives in `./components` construct it;
28
- * nothing an author passes as a string reaches this node.
29
- */
30
- {
31
- readonly type: "Raw";
32
- readonly html: string;
33
- readonly text: string;
34
- readonly origins: readonly JsxSourceLocation[];
35
- };
36
- type EmailNodeOf<Type extends EmailNode["type"]> = Extract<EmailNode, {
37
- readonly type: Type;
38
- }>;
39
- type EmailNodeFields<Type extends EmailNode["type"]> = Omit<EmailNodeOf<Type>, "type">;
40
- /** Constructors for the authoring tree. The tree is data; nothing here validates it. */
41
- declare const EmailNode: {
42
- readonly Text: (fields: EmailNodeFields<"Text">) => EmailNodeOf<"Text">;
43
- readonly Element: (fields: EmailNodeFields<"Element">) => EmailNodeOf<"Element">;
44
- readonly Fragment: (fields: EmailNodeFields<"Fragment">) => EmailNodeOf<"Fragment">;
45
- readonly Raw: (fields: EmailNodeFields<"Raw">) => EmailNodeOf<"Raw">;
46
- };
47
- type EmailChild = EmailNode | string | number | boolean | null | undefined | readonly EmailChild[];
48
- type EmailComponent<P = never> = (props: P) => EmailChild;
49
- declare const Fragment: unique symbol;
50
- /** Call sites enclosing the element being built, innermost first. */
51
- declare const currentEmailOrigins: () => readonly JsxSourceLocation[];
52
- /** Record a finding from inside a compiler primitive, located at the authoring call site. */
53
- declare const reportEmailDiagnostic: (diagnostic: Omit<EmailDiagnostic, "origins">) => void;
54
- /** Take and clear the findings primitives reported during a render. */
55
- declare const drainEmailDiagnostics: () => readonly EmailDiagnostic[];
56
- /** Normalize actual values without interpolation or recording proxies. */
57
- declare const normalizeChildren: (input: unknown) => EmailNode[];
58
- /** Compatibility markup emitted by a compiler primitive, with the text it stands for. */
59
- declare const rawMarkup: (html: string, text?: string) => EmailNode;
60
- /** Automatic JSX factory for ordinary HTML and synchronous imported components. */
61
- declare const jsx: (type: string | EmailComponent<Record<string, unknown>> | typeof Fragment, props: Record<string, unknown> | null) => EmailNode;
62
- declare const jsxs: typeof jsx;
63
- declare namespace JSX {
64
- type Element = EmailNode;
65
- type ElementType = string | EmailComponent | typeof Fragment;
66
- interface ElementChildrenAttribute {
67
- readonly children: object;
68
- }
69
- interface IntrinsicAttributes {
70
- readonly key?: string | number | undefined;
71
- }
72
- interface IntrinsicElements extends EmailIntrinsicElements {}
73
- }
74
- //#endregion
75
3
  //#region src/email/elements.d.ts
76
4
  /** How one attribute is spelled and what values it accepts. */
77
5
  interface EmailAttributeSpec {
@@ -695,5 +623,77 @@ type ChildrenProp<Content extends EmailElementSpec["content"]> = Content extends
695
623
  /** Props each supported tag accepts, derived from `EMAIL_ELEMENTS`. */
696
624
  type EmailIntrinsicElements = { readonly [Tag in EmailTag]: EmailGlobalProps & AttributeProps<typeof EMAIL_GLOBAL_ATTRIBUTES> & AttributeProps<(typeof EMAIL_ELEMENTS)[Tag]["attributes"]> & ChildrenProp<(typeof EMAIL_ELEMENTS)[Tag]["content"]>; };
697
625
  //#endregion
698
- export { reportEmailDiagnostic as C, rawMarkup as S, currentEmailOrigins as _, EmailGlobalProps as a, jsxs as b, EmailStyle as c, isUrlAttribute as d, EmailChild as f, JSX as g, Fragment as h, EmailElementSpec as i, EmailTag as l, EmailNode as m, EMAIL_GLOBAL_ATTRIBUTES as n, EmailIntrinsicElements as o, EmailComponent as p, EmailAttributeSpec as r, EmailLayout as s, EMAIL_ELEMENTS as t, emailElement as u, drainEmailDiagnostics as v, normalizeChildren as x, jsx as y };
699
- //# sourceMappingURL=elements-cqldpaJt.d.ts.map
626
+ //#region src/email/jsx-runtime.d.ts
627
+ /** Derived authoring tree; source stays canonical. Origins run from inner element to component call sites. */
628
+ type EmailNode = {
629
+ readonly type: "Text";
630
+ readonly value: string;
631
+ } | {
632
+ readonly type: "Element";
633
+ readonly tag: string;
634
+ readonly props: Readonly<Record<string, unknown>>;
635
+ readonly children: readonly EmailNode[];
636
+ readonly origins: readonly JsxSourceLocation[];
637
+ /**
638
+ * True when this element is written at `origins[0]`; false when a compiler
639
+ * primitive generated it and `origins[0]` is the call site that asked for
640
+ * it. Both point at the same authoring target, but only an authored element
641
+ * can have its own props edited there.
642
+ */
643
+ readonly authored: boolean;
644
+ } | {
645
+ readonly type: "Fragment";
646
+ readonly children: readonly EmailNode[];
647
+ } |
648
+ /**
649
+ * Compatibility markup a compiler primitive emits verbatim — conditional
650
+ * comments and VML. Only the primitives in `./components` construct it;
651
+ * nothing an author passes as a string reaches this node.
652
+ */
653
+ {
654
+ readonly type: "Raw";
655
+ readonly html: string;
656
+ readonly text: string;
657
+ readonly origins: readonly JsxSourceLocation[];
658
+ };
659
+ type EmailNodeOf<Type extends EmailNode["type"]> = Extract<EmailNode, {
660
+ readonly type: Type;
661
+ }>;
662
+ type EmailNodeFields<Type extends EmailNode["type"]> = Omit<EmailNodeOf<Type>, "type">;
663
+ /** Constructors for the authoring tree. The tree is data; nothing here validates it. */
664
+ declare const EmailNode: {
665
+ readonly Text: (fields: EmailNodeFields<"Text">) => EmailNodeOf<"Text">;
666
+ readonly Element: (fields: EmailNodeFields<"Element">) => EmailNodeOf<"Element">;
667
+ readonly Fragment: (fields: EmailNodeFields<"Fragment">) => EmailNodeOf<"Fragment">;
668
+ readonly Raw: (fields: EmailNodeFields<"Raw">) => EmailNodeOf<"Raw">;
669
+ };
670
+ type EmailChild = EmailNode | string | number | boolean | null | undefined | readonly EmailChild[];
671
+ type EmailComponent<P = never> = (props: P) => EmailChild;
672
+ declare const Fragment: unique symbol;
673
+ /** Call sites enclosing the element being built, innermost first. */
674
+ declare const currentEmailOrigins: () => readonly JsxSourceLocation[];
675
+ /** Record a finding from inside a compiler primitive, located at the authoring call site. */
676
+ declare const reportEmailDiagnostic: (diagnostic: Omit<EmailDiagnostic, "origins">) => void;
677
+ /** Take and clear the findings primitives reported during a render. */
678
+ declare const drainEmailDiagnostics: () => readonly EmailDiagnostic[];
679
+ /** Normalize actual values without interpolation or recording proxies. */
680
+ declare const normalizeChildren: (input: unknown) => EmailNode[];
681
+ /** Compatibility markup emitted by a compiler primitive, with the text it stands for. */
682
+ declare const rawMarkup: (html: string, text?: string) => EmailNode;
683
+ /** Automatic JSX factory for ordinary HTML and synchronous imported components. */
684
+ declare const jsx: (type: string | EmailComponent<Record<string, unknown>> | typeof Fragment, props: Record<string, unknown> | null) => EmailNode;
685
+ declare const jsxs: typeof jsx;
686
+ declare namespace JSX {
687
+ type Element = EmailNode;
688
+ type ElementType = string | EmailComponent | typeof Fragment;
689
+ interface ElementChildrenAttribute {
690
+ readonly children: object;
691
+ }
692
+ interface IntrinsicAttributes {
693
+ readonly key?: string | number | undefined;
694
+ }
695
+ interface IntrinsicElements extends EmailIntrinsicElements {}
696
+ }
697
+ //#endregion
698
+ export { isUrlAttribute as C, emailElement as S, EmailGlobalProps as _, JSX as a, EmailStyle as b, jsx as c, rawMarkup as d, reportEmailDiagnostic as f, EmailElementSpec as g, EmailAttributeSpec as h, Fragment as i, jsxs as l, EMAIL_GLOBAL_ATTRIBUTES as m, EmailComponent as n, currentEmailOrigins as o, EMAIL_ELEMENTS as p, EmailNode as r, drainEmailDiagnostics as s, EmailChild as t, normalizeChildren as u, EmailIntrinsicElements as v, EmailTag as x, EmailLayout as y };
699
+ //# sourceMappingURL=jsx-runtime-ylIOnaSf.d.ts.map
@@ -1,20 +1,113 @@
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
- import { TEMPLATE_ID_PATTERN, TEMPLATE_NAME_PATTERN } from "./template.js";
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
5
  import { FORMATTER_NAMES } from "./fmt.js";
5
- import { isAssetPath } from "./email/assets.js";
6
- import { buildLineMap, lineColumn } from "./diagnostics.js";
7
- import { n as RESERVED_FOOTER_ATTRIBUTES, t as BRAND_FOOTER_MARKER } from "./unsubscribe-link-CoesnQDU.js";
8
- import { EmailNode, drainEmailDiagnostics } from "./email/jsx-runtime.js";
9
- import { brandComponents } from "./email/brand.js";
10
- import { t as applyStylesheet } from "./cascade-t6kxlgEI.js";
11
- import { i as resolveFromDirectory, r as projectDirectory } from "./theme-imports-lvBw3AD1.js";
12
- import "./ir.js";
13
- import { a as WHATSAPP_HEADER_TYPES, i as WHATSAPP_CATEGORIES, n as SMS_CATEGORIES, r as WHATSAPP_ATTRIBUTES, t as SMS_ATTRIBUTES } from "./channel-specs-B6M_HlaV.js";
14
- import { EMAIL_ELEMENTS, emailElement } from "./email/elements.js";
15
- import { checkAttribute, serializeStyle } from "./email/html.js";
16
- import { Button, Column, Columns, Divider, Email, Image, Link, Section, Spacer, equalColumnWidth } from "./email/components.js";
17
- import { parse } from "@babel/parser";
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 lineColumn, t as buildLineMap } from "./diagnostic-model-DJtqbETd.js";
10
+ import { l as isAssetPath, n as brandComponents } from "./brand-DTctcDmf.js";
11
+ import { parse } from "css-tree";
12
+ import { parse as parse$1 } from "@babel/parser";
13
+ //#region src/email/theme-imports.ts
14
+ /** The directory part of a project path, with no trailing slash (`""` at the root). */
15
+ const projectDirectory = (path) => path.includes("/") ? path.slice(0, path.lastIndexOf("/")) : "";
16
+ /** A relative or root-absolute stylesheet reference, resolved from a project directory. */
17
+ const resolveFromDirectory = (directory, reference) => {
18
+ const segments = reference.startsWith("/") || directory === "" ? [] : directory.split("/");
19
+ for (const segment of reference.split("/")) {
20
+ if (segment === "" || segment === ".") continue;
21
+ if (segment === "..") {
22
+ if (segments.length === 0) return void 0;
23
+ segments.pop();
24
+ } else segments.push(segment);
25
+ }
26
+ return segments.length === 0 ? void 0 : segments.join("/");
27
+ };
28
+ const importTarget = (node) => {
29
+ if (node.type !== "Atrule" || node.prelude === null || node.prelude.type !== "AtrulePrelude") return { extra: true };
30
+ const parts = [...node.prelude.children].filter((part) => part.type !== "WhiteSpace");
31
+ const first = parts[0];
32
+ const target = first?.type === "String" ? first.value : first?.type === "Url" ? first.value : void 0;
33
+ return target === void 0 ? { extra: true } : {
34
+ target,
35
+ extra: parts.length > 1
36
+ };
37
+ };
38
+ /**
39
+ * Walk the theme's `@import` graph. Project `.css` files resolve from the
40
+ * importing file's directory; the plugin's brand specifier, bare or with a
41
+ * slug, is recorded for the brand the build supplies. Anything else is an
42
+ * error.
43
+ */
44
+ const resolveThemeImports = (themePath, themeCss, files, brandPlugin) => {
45
+ const brand = JSON.stringify(brandPlugin.specifier);
46
+ const stylesheets = /* @__PURE__ */ new Map([[themePath, themeCss]]);
47
+ const brandSpecifiers = /* @__PURE__ */ new Set();
48
+ const diagnostics = [];
49
+ const visit = (path, source, chain) => {
50
+ const lineMap = buildLineMap(source);
51
+ const ast = parse(source, {
52
+ positions: true,
53
+ parseValue: false,
54
+ parseRulePrelude: false
55
+ });
56
+ if (ast.type !== "StyleSheet") return;
57
+ for (const node of ast.children) {
58
+ if (node.type !== "Atrule" || node.name.toLowerCase() !== "import") continue;
59
+ const position = lineColumn(lineMap, node.loc?.start.offset ?? 0);
60
+ const origins = [{
61
+ fileName: path,
62
+ lineNumber: position.line,
63
+ columnNumber: position.column
64
+ }];
65
+ const refuse = (message) => diagnostics.push({
66
+ code: "css-import-unresolved",
67
+ severity: "error",
68
+ message,
69
+ origins
70
+ });
71
+ const { target, extra } = importTarget(node);
72
+ if (target === void 0) {
73
+ refuse(`${path}: @import needs a quoted stylesheet path or ${brand}.`);
74
+ continue;
75
+ }
76
+ if (extra) {
77
+ refuse(`${path}: @import ${JSON.stringify(target)} takes no layer, supports or media conditions; the import order is the layering.`);
78
+ continue;
79
+ }
80
+ if (parseBrandSpecifier(target, brandPlugin) !== void 0) {
81
+ brandSpecifiers.add(target);
82
+ continue;
83
+ }
84
+ if (!/^\.{0,2}\//.test(target) || !/\.css$/i.test(target.replace(/[?#].*$/, ""))) {
85
+ refuse(`${path}: @import ${JSON.stringify(target)} is not a project stylesheet or ${brand}. The compiler supplies Tailwind itself; import project .css files by relative path.`);
86
+ continue;
87
+ }
88
+ const resolved = resolveFromDirectory(projectDirectory(path), target.replace(/[?#].*$/, ""));
89
+ const content = resolved === void 0 ? void 0 : files[resolved];
90
+ if (resolved === void 0 || typeof content !== "string") {
91
+ refuse(`${path}: @import ${JSON.stringify(target)} names no stylesheet in the project.`);
92
+ continue;
93
+ }
94
+ if (chain.includes(resolved)) {
95
+ refuse(`${path}: @import ${JSON.stringify(target)} imports itself through ${chain.join(" → ")}.`);
96
+ continue;
97
+ }
98
+ if (stylesheets.has(resolved)) continue;
99
+ stylesheets.set(resolved, content);
100
+ visit(resolved, content, [...chain, resolved]);
101
+ }
102
+ };
103
+ visit(themePath, themeCss, [themePath]);
104
+ return {
105
+ stylesheets,
106
+ brandSpecifiers: [...brandSpecifiers].sort(),
107
+ diagnostics
108
+ };
109
+ };
110
+ //#endregion
18
111
  //#region src/sml/diagnostics.ts
19
112
  /** Collects the findings of one compile. Every finding carries a code, a location and a fix. */
20
113
  var Findings = class {
@@ -125,7 +218,7 @@ const parseSource = (path, source) => {
125
218
  const position = (offset) => lineColumn(lines, offset);
126
219
  let program;
127
220
  try {
128
- program = parse(source, {
221
+ program = parse$1(source, {
129
222
  plugins: ["typescript", "jsx"],
130
223
  sourceType: "module"
131
224
  }).program;
@@ -163,6 +256,32 @@ const isValueNode = (node) => {
163
256
  return true;
164
257
  };
165
258
  //#endregion
259
+ //#region src/channel-specs.ts
260
+ const SMS_CATEGORIES = [
261
+ "transactional",
262
+ "promotional",
263
+ "service_implicit"
264
+ ];
265
+ const WHATSAPP_CATEGORIES = [
266
+ "marketing",
267
+ "utility",
268
+ "authentication"
269
+ ];
270
+ const WHATSAPP_HEADER_TYPES = [
271
+ "text",
272
+ "image",
273
+ "video",
274
+ "document"
275
+ ];
276
+ /** The attributes `<Sms>` accepts. */
277
+ const SMS_ATTRIBUTES = ["category"];
278
+ /** The attributes `<WhatsApp>` accepts. */
279
+ const WHATSAPP_ATTRIBUTES = [
280
+ "name",
281
+ "language",
282
+ "category"
283
+ ];
284
+ //#endregion
166
285
  //#region src/whatsapp-template.ts
167
286
  const isConcat = (value) => typeof value === "object" && value !== null && "concat" in value;
168
287
  /**
@@ -968,7 +1087,7 @@ const columnsEach = (call, bindings) => {
968
1087
  /** Run a compiler primitive at compile time and lower what it returns. */
969
1088
  const runBuiltinComponent = (call) => {
970
1089
  const { lowerer, ctx, node, binding } = call;
971
- const specifier = binding.kind === "markup" ? binding.specifier : "samva:brand";
1090
+ const specifier = binding.specifier;
972
1091
  if (binding.kind === "markup" && (specifier === "@samva/markup/sms" || specifier === "@samva/markup/whatsapp")) {
973
1092
  const lowered = call.channel?.(call);
974
1093
  if (lowered !== void 0) return lowered;
@@ -976,20 +1095,20 @@ const runBuiltinComponent = (call) => {
976
1095
  return [];
977
1096
  }
978
1097
  const exported = binding.imported;
979
- const isEmail = binding.kind === "markup" && specifier === "@samva/markup/email/components";
1098
+ const isEmail = binding.kind === "markup" && specifier === "@samva/markup/email";
980
1099
  const isBrand = binding.kind === "brand";
981
1100
  if (!(isEmail || isBrand)) {
982
- lowerer.error(ctx, node, "unknown-component", `<${call.name}> is not a component the profile can read.`, "Import components from @samva/markup/email/components, samva:brand, or the project.");
1101
+ lowerer.error(ctx, node, "unknown-component", `<${call.name}> is not a component the profile can read.`, `Import components from @samva/markup/email, ${call.brandPlugin.specifier}, or the project.`);
983
1102
  return [];
984
1103
  }
985
1104
  let fn;
986
1105
  if (isEmail) fn = COMPONENTS[exported];
987
1106
  else {
988
1107
  if (call.brand === void 0) {
989
- lowerer.error(ctx, node, "brand-unavailable", `<${call.name}> comes from samva:brand, and this build has no brand to resolve it.`, "Compile with a brand, or remove the brand components.");
1108
+ lowerer.error(ctx, node, "brand-unavailable", `<${call.name}> comes from ${binding.specifier}, and this build has no brand to resolve it.`, "Compile with a brand, or remove the brand components.");
990
1109
  return [];
991
1110
  }
992
- const brand = brandComponents(call.brand);
1111
+ const brand = brandComponents(call.brand, call.brandPlugin);
993
1112
  fn = exported === "BrandLogo" ? brand.BrandLogo : exported === "BrandFooter" ? brand.BrandFooter : void 0;
994
1113
  }
995
1114
  if (fn === void 0 || !Object.hasOwn(COMPONENTS, exported) && isEmail) {
@@ -1281,8 +1400,10 @@ var Lowerer = class {
1281
1400
  options;
1282
1401
  sources = [];
1283
1402
  uniqueId = 0;
1403
+ reservedAttributes;
1284
1404
  constructor(options) {
1285
1405
  this.options = options;
1406
+ this.reservedAttributes = reservedBrandAttributes(options.brandPlugin);
1286
1407
  }
1287
1408
  get findings() {
1288
1409
  return this.options.findings;
@@ -2004,8 +2125,8 @@ var Lowerer = class {
2004
2125
  this.error(ctx, attribute, isHtml ? "dangerous-html" : name === "ref" ? "unsupported-attribute" : "event-handler", isHtml ? "`dangerouslySetInnerHTML` injects unescaped markup." : name === "ref" ? "A `ref` needs a running component." : `\`${name}\` is an event handler; email clients strip scripting and Samva refuses it.`, isHtml ? "Write the markup as elements." : "Remove it.");
2005
2126
  continue;
2006
2127
  }
2007
- if (RESERVED_FOOTER_ATTRIBUTES.has(name)) {
2008
- this.error(ctx, attribute, "reserved-attribute", `\`${name}\` is written only by BrandFooter from samva:brand.`, "Render <BrandFooter /> instead of marking an element as the footer.");
2128
+ if (this.reservedAttributes.has(name)) {
2129
+ this.error(ctx, attribute, "reserved-attribute", `\`${name}\` is written only by BrandFooter from ${this.options.brandPlugin.specifier}.`, "Render <BrandFooter /> instead of marking an element as the footer.");
2009
2130
  continue;
2010
2131
  }
2011
2132
  if (name === "className" || name === "class") {
@@ -2029,7 +2150,7 @@ var Lowerer = class {
2029
2150
  }
2030
2151
  if (value === void 0) continue;
2031
2152
  if (value === false || value === null) continue;
2032
- if (value === "%samva:unsubscribe-url%") {
2153
+ if (value === this.options.brandPlugin.unsubscribeUrlPlaceholder) {
2033
2154
  this.error(ctx, attribute, "reserved-url", "That URL is the unsubscribe placeholder only BrandFooter can write.", "Render <BrandFooter /> instead.");
2034
2155
  continue;
2035
2156
  }
@@ -2176,6 +2297,7 @@ var Lowerer = class {
2176
2297
  name,
2177
2298
  binding: imported,
2178
2299
  brand: this.options.brand,
2300
+ brandPlugin: this.options.brandPlugin,
2179
2301
  channel: this.options.channel
2180
2302
  });
2181
2303
  let fn;
@@ -2187,7 +2309,7 @@ var Lowerer = class {
2187
2309
  value: local.value
2188
2310
  };
2189
2311
  if (fn === void 0 || !/Function|Arrow/.test(fn.value.type)) {
2190
- this.error(ctx, node, "unknown-component", `<${name}> is not a component the profile can read.`, "Import it from @samva/markup/email/components, or from a project file that exports it as a function.");
2312
+ this.error(ctx, node, "unknown-component", `<${name}> is not a component the profile can read.`, "Import it from @samva/markup/email, or from a project file that exports it as a function.");
2191
2313
  return [];
2192
2314
  }
2193
2315
  return this.partial(ctx, node, attributes, name, fn.module, fn.value);
@@ -3217,7 +3339,7 @@ const lowerProject = (options, project, findings, inputs) => {
3217
3339
  staticContext,
3218
3340
  assetUrl: urlFor,
3219
3341
  brand: options.brand,
3220
- brand: options.brand,
3342
+ brandPlugin: options.brandPlugin ?? samvaBrandPlugin,
3221
3343
  channel: channelLowering
3222
3344
  });
3223
3345
  const rootContext = (target) => ({
@@ -3407,6 +3529,8 @@ const lowerProject = (options, project, findings, inputs) => {
3407
3529
  //#region src/sml/project.ts
3408
3530
  const MARKUP_SPECIFIER = "@samva/markup";
3409
3531
  const MARKUP_PREFIX = "@samva/markup/";
3532
+ /** Entries a template could import that moved; each is reported with its replacement. */
3533
+ const MOVED_MARKUP_ENTRIES = { "@samva/markup/email/components": "@samva/markup/email" };
3410
3534
  const SCRIPT_EXTENSIONS = [".tsx", ".ts"];
3411
3535
  /** Resolve a relative specifier to a project file, or `undefined`. */
3412
3536
  const resolveSpecifier = (from, specifier, files) => {
@@ -3419,7 +3543,7 @@ const resolveSpecifier = (from, specifier, files) => {
3419
3543
  const isScriptPath = (path) => /\.[cm]?[jt]sx?$/.test(path);
3420
3544
  const declaredNames = (pattern) => pattern.type === "Identifier" ? [text(pattern, "name") ?? ""] : [];
3421
3545
  /** Load the entry and everything it imports from the project. */
3422
- const loadProject = (files, entryPath, findings, loadOptions = {}) => {
3546
+ const loadProject = (files, entryPath, findings, loadOptions) => {
3423
3547
  const modules = /* @__PURE__ */ new Map();
3424
3548
  const unresolved = /* @__PURE__ */ new Set();
3425
3549
  const loading = /* @__PURE__ */ new Set();
@@ -3486,6 +3610,12 @@ const loadProject = (files, entryPath, findings, loadOptions = {}) => {
3486
3610
  const imported = child(node, "imported");
3487
3611
  return imported === void 0 ? local(node) : text(imported, "name") ?? text(imported, "value") ?? local(node);
3488
3612
  };
3613
+ const moved = MOVED_MARKUP_ENTRIES[specifier];
3614
+ if (moved !== void 0) {
3615
+ findings.add(parsed, child(statement, "source") ?? statement, "moved-import", `\`${specifier}\` moved to \`${moved}\`; the imported names are unchanged.`, `Import from "${moved}".`);
3616
+ failed = true;
3617
+ break;
3618
+ }
3489
3619
  if (specifier === MARKUP_SPECIFIER || specifier.startsWith(MARKUP_PREFIX)) {
3490
3620
  for (const item of specifiers) {
3491
3621
  if (text(item, "importKind") === "type") continue;
@@ -3498,7 +3628,7 @@ const loadProject = (files, entryPath, findings, loadOptions = {}) => {
3498
3628
  }
3499
3629
  break;
3500
3630
  }
3501
- if (/^samva:brand(\/|$)/.test(specifier)) {
3631
+ if (parseBrandSpecifier(specifier, loadOptions.brandPlugin) !== void 0) {
3502
3632
  for (const item of specifiers) if (text(item, "importKind") !== "type") imports.set(local(item), {
3503
3633
  kind: "brand",
3504
3634
  specifier,
@@ -3631,13 +3761,17 @@ const asFiles = (files, entry) => typeof files === "string" ? { [entry]: files }
3631
3761
  * and the entry's path. Imports of files that are absent are trusted to exist, and the brand,
3632
3762
  * Tailwind and theme are not resolved, so the result holds only findings the source itself owns.
3633
3763
  */
3634
- const checkStaticProfile = (files, entry = DEFAULT_ENTRY) => {
3764
+ const checkStaticProfile = (files, entry = DEFAULT_ENTRY, brandPlugin = samvaBrandPlugin) => {
3635
3765
  const findings = new Findings();
3636
- const project = loadProject(asFiles(files, entry), entry, findings, { lenient: true });
3766
+ const project = loadProject(asFiles(files, entry), entry, findings, {
3767
+ brandPlugin,
3768
+ lenient: true
3769
+ });
3637
3770
  if (project === void 0) return findings.items;
3638
3771
  lowerProject({
3639
3772
  entry,
3640
- brand: void 0
3773
+ brand: void 0,
3774
+ brandPlugin
3641
3775
  }, project, findings, {
3642
3776
  assets: [],
3643
3777
  stylesheets: /* @__PURE__ */ new Map(),
@@ -3665,6 +3799,6 @@ const introducedProfileErrors = (before, after) => {
3665
3799
  return added;
3666
3800
  };
3667
3801
  //#endregion
3668
- export { lowerProject as a, completed as i, introducedProfileErrors as n, Findings as o, loadProject as r, checkStaticProfile as t };
3802
+ export { lowerProject as a, WHATSAPP_HEADER_TYPES as c, resolveFromDirectory as d, resolveThemeImports as f, completed as i, Findings as l, introducedProfileErrors as n, SMS_CATEGORIES as o, loadProject as r, WHATSAPP_CATEGORIES as s, checkStaticProfile as t, projectDirectory as u };
3669
3803
 
3670
- //# sourceMappingURL=profile-BH-OtdDA.js.map
3804
+ //# sourceMappingURL=profile-PUfi3GPH.js.map