@samva/markup 0.8.0 → 0.10.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 (172) hide show
  1. package/AGENTS.md +107 -46
  2. package/CHANGELOG.md +345 -0
  3. package/README.md +188 -52
  4. package/dist/{brand-DcoVkh_S.d.ts → assets-BIsyMQ92.d.ts} +41 -11
  5. package/dist/{email/brand.js → brand-C0MKTwmX.js} +64 -4
  6. package/dist/{theme-css.d.ts → brand.d.ts} +12 -3
  7. package/dist/brand.js +4 -0
  8. package/dist/channel-specs-C2feJwuP.d.ts +13 -0
  9. package/dist/compiler.d.ts +88 -115
  10. package/dist/compiler.js +750 -171
  11. package/dist/components-BZCqegS8.js +1864 -0
  12. package/dist/diagnostic-model-DJtqbETd.js +38 -0
  13. package/dist/{diagnostics-OcQKQYSY.d.ts → diagnostic-model-DxthUZBT.d.ts} +3 -21
  14. package/dist/{diagnostics-BMYtBCQk.d.ts → diagnostics-B-Gym34R.d.ts} +8 -4
  15. package/dist/{email/diagnostics.js → diagnostics-CnKFefKz.js} +4 -3
  16. package/dist/diagnostics.d.ts +410 -2
  17. package/dist/diagnostics.js +402 -52
  18. package/dist/edit.d.ts +234 -0
  19. package/dist/edit.js +947 -0
  20. package/dist/email/jsx-dev-runtime.d.ts +2 -2
  21. package/dist/email/jsx-dev-runtime.js +1 -2
  22. package/dist/email/jsx-runtime.d.ts +1 -1
  23. package/dist/email/jsx-runtime.js +1 -85
  24. package/dist/{email/components.d.ts → email.d.ts} +22 -11
  25. package/dist/email.js +2 -0
  26. package/dist/fmt.d.ts +32 -0
  27. package/dist/fmt.js +174 -0
  28. package/dist/index.d.ts +2 -0
  29. package/dist/index.js +2 -0
  30. package/dist/{render-vAv_4r9v.js → ir-CLhlklKV.js} +96 -279
  31. package/dist/jsx-runtime-CBlexoeR.js +98 -0
  32. package/dist/{elements-BzjjnoCS.d.ts → jsx-runtime-ylIOnaSf.d.ts} +76 -76
  33. package/dist/profile-pWGtWZXF.js +3803 -0
  34. package/dist/render-BPonDpCl.d.ts +539 -0
  35. package/dist/render-BQmfh94k.js +257 -0
  36. package/dist/render.d.ts +35 -0
  37. package/dist/render.js +49 -0
  38. package/dist/sms.d.ts +16 -0
  39. package/dist/sms.js +12 -0
  40. package/dist/source-locations-C8wCsiAy.d.ts +23 -0
  41. package/dist/template-DMPwxMSq.d.ts +65 -0
  42. package/dist/template-zLUjcwfF.js +42 -0
  43. package/dist/{theme-css-hFqcPSar.js → theme-css-JtRCm1fb.js} +2 -2
  44. package/dist/whatsapp.d.ts +57 -0
  45. package/dist/whatsapp.js +22 -0
  46. package/generated/contract.json +1788 -0
  47. package/generated/reference.md +360 -0
  48. package/package.json +46 -239
  49. package/samva-brand.d.ts +4 -4
  50. package/skills/sml/SKILL.md +123 -0
  51. package/dist/ast-BrDtApwS.d.ts +0 -107
  52. package/dist/ast.d.ts +0 -2
  53. package/dist/ast.js +0 -124
  54. package/dist/candidate.d.ts +0 -29
  55. package/dist/candidate.js +0 -88
  56. package/dist/catalog-CKuOooB8.d.ts +0 -354
  57. package/dist/checks-D73Xizbw.js +0 -59366
  58. package/dist/checks.d.ts +0 -33
  59. package/dist/checks.js +0 -2
  60. package/dist/classes.d.ts +0 -26
  61. package/dist/classes.js +0 -184
  62. package/dist/compile-sms.d.ts +0 -28
  63. package/dist/compile-sms.js +0 -44
  64. package/dist/compile-whatsapp.d.ts +0 -56
  65. package/dist/compile-whatsapp.js +0 -186
  66. package/dist/compile.d.ts +0 -18
  67. package/dist/compile.js +0 -29
  68. package/dist/components.d.ts +0 -289
  69. package/dist/components.js +0 -106
  70. package/dist/conditionals.d.ts +0 -10
  71. package/dist/conditionals.js +0 -74
  72. package/dist/corpus-CPPfgAHN.d.ts +0 -106
  73. package/dist/css-CS2jLID6.d.ts +0 -109
  74. package/dist/email/assets.d.ts +0 -40
  75. package/dist/email/assets.js +0 -63
  76. package/dist/email/brand-footer.d.ts +0 -34
  77. package/dist/email/brand-footer.js +0 -62
  78. package/dist/email/brand.d.ts +0 -2
  79. package/dist/email/components.js +0 -240
  80. package/dist/email/diagnostics.d.ts +0 -2
  81. package/dist/email/elements.d.ts +0 -2
  82. package/dist/email/elements.js +0 -299
  83. package/dist/email/html.d.ts +0 -2
  84. package/dist/email/html.js +0 -278
  85. package/dist/email/render.d.ts +0 -50
  86. package/dist/email/render.js +0 -3
  87. package/dist/email/selection.d.ts +0 -2
  88. package/dist/email/selection.js +0 -91
  89. package/dist/email/source-edit.d.ts +0 -67
  90. package/dist/email/source-edit.js +0 -185
  91. package/dist/email/stylesheets.d.ts +0 -15
  92. package/dist/email/stylesheets.js +0 -48
  93. package/dist/email/text.d.ts +0 -7
  94. package/dist/email/text.js +0 -100
  95. package/dist/emit.d.ts +0 -51
  96. package/dist/emit.js +0 -151
  97. package/dist/html-Bl9BAdqc.d.ts +0 -42
  98. package/dist/interpolate.d.ts +0 -24
  99. package/dist/interpolate.js +0 -43
  100. package/dist/jsx-dev-runtime.d.ts +0 -11
  101. package/dist/jsx-dev-runtime.js +0 -15
  102. package/dist/jsx-runtime-I-1nCtl2.d.ts +0 -57
  103. package/dist/jsx-runtime.d.ts +0 -2
  104. package/dist/jsx-runtime.js +0 -121
  105. package/dist/lexer.d.ts +0 -60
  106. package/dist/lexer.js +0 -281
  107. package/dist/model.d.ts +0 -24
  108. package/dist/model.js +0 -77
  109. package/dist/ops-BvwqHl7V.js +0 -630
  110. package/dist/ops.d.ts +0 -114
  111. package/dist/ops.js +0 -2
  112. package/dist/parser-t29cHLVL.d.ts +0 -396
  113. package/dist/parser.d.ts +0 -2
  114. package/dist/parser.js +0 -859
  115. package/dist/predicate-BI6uRRM-.js +0 -549
  116. package/dist/predicate-M-05Nd7R.d.ts +0 -56
  117. package/dist/predicate.d.ts +0 -2
  118. package/dist/predicate.js +0 -2
  119. package/dist/printer.d.ts +0 -6
  120. package/dist/printer.js +0 -122
  121. package/dist/registry-Dcp6QEK_.d.ts +0 -73
  122. package/dist/registry.d.ts +0 -2
  123. package/dist/registry.js +0 -646
  124. package/dist/render-template.d.ts +0 -45
  125. package/dist/render-template.js +0 -76
  126. package/dist/selection-CCArx9lU.d.ts +0 -91
  127. package/dist/showcase-egkusQBf.d.ts +0 -415
  128. package/dist/sms-segments-y4FpPilJ.d.ts +0 -13
  129. package/dist/sms-segments.d.ts +0 -2
  130. package/dist/sms-segments.js +0 -46
  131. package/dist/source-locations-DrCM82Ym.d.ts +0 -67
  132. package/dist/source-locations.d.ts +0 -2
  133. package/dist/source-locations.js +0 -102
  134. package/dist/starters/category-mapping.d.ts +0 -13
  135. package/dist/starters/category-mapping.js +0 -49
  136. package/dist/starters/corpus.d.ts +0 -2
  137. package/dist/starters/corpus.js +0 -194
  138. package/dist/starters/library.d.ts +0 -18
  139. package/dist/starters/library.js +0 -692
  140. package/dist/starters/public-gallery.d.ts +0 -266
  141. package/dist/starters/public-gallery.js +0 -916
  142. package/dist/starters/public-handoff.d.ts +0 -164
  143. package/dist/starters/public-handoff.js +0 -361
  144. package/dist/starters/public-preview-artifacts.d.ts +0 -43
  145. package/dist/starters/public-preview-artifacts.js +0 -104
  146. package/dist/starters/public-preview.d.ts +0 -61
  147. package/dist/starters/public-preview.js +0 -83
  148. package/dist/starters/showcase.d.ts +0 -2
  149. package/dist/starters/showcase.js +0 -335
  150. package/dist/starters/sources.d.ts +0 -45
  151. package/dist/starters/sources.js +0 -7762
  152. package/dist/starters/theme.d.ts +0 -45
  153. package/dist/starters/theme.js +0 -77
  154. package/dist/starters.d.ts +0 -2
  155. package/dist/starters.js +0 -874
  156. package/dist/tailwind-BYq7Lo6u.js +0 -727
  157. package/dist/template-DWftqh4x.d.ts +0 -60
  158. package/dist/template.d.ts +0 -2
  159. package/dist/template.js +0 -37
  160. package/dist/theme-css.js +0 -2
  161. package/dist/tsx-source-instrumentation.d.ts +0 -30
  162. package/dist/tsx-source-instrumentation.js +0 -115
  163. package/dist/tsx-tracer.d.ts +0 -112
  164. package/dist/tsx-tracer.js +0 -985
  165. package/dist/variables-J_L3qxRa.d.ts +0 -77
  166. package/dist/variables.d.ts +0 -2
  167. package/dist/variables.js +0 -141
  168. package/dist/version-BITr58kk.js +0 -96
  169. package/generated/sml-agent-contract.json +0 -709
  170. package/generated/sml-email-agent-contract.json +0 -615
  171. package/generated/sml-email-language-reference.md +0 -94
  172. package/generated/sml-language-reference.md +0 -195
package/dist/checks.d.ts DELETED
@@ -1,33 +0,0 @@
1
- import { t as Diagnostic } from "./diagnostics-OcQKQYSY.js";
2
- //#region src/checks.d.ts
3
- type EmailClientDarkBehavior = "honors" | "force-inverts" | "no dark mode";
4
- interface EmailClientTarget {
5
- readonly family: string;
6
- readonly platform: string;
7
- readonly label: string;
8
- readonly darkBehavior: EmailClientDarkBehavior;
9
- }
10
- interface TargetClientProfile {
11
- readonly id: string;
12
- readonly label: string;
13
- readonly targets: readonly EmailClientTarget[];
14
- }
15
- interface EmailCompatibilityInput {
16
- readonly source: string;
17
- readonly profile?: TargetClientProfile | undefined;
18
- readonly declaredVariables?: readonly string[] | undefined;
19
- }
20
- declare const DEFAULT_EMAIL_TARGET_CLIENT_PROFILE: TargetClientProfile;
21
- /**
22
- * Finds site-relative assets that work in Vite preview but cannot resolve
23
- * after an email leaves the author's site. Values backed entirely by one
24
- * variable are excluded because their runtime value is a delivery concern.
25
- */
26
- declare const checkDeliveryAssetDiagnostics: (source: string) => readonly Diagnostic[];
27
- declare function isBreakingCompatibilityCheck(message: string): boolean;
28
- declare function collectSmlFeatureSlugs(source: string): readonly string[];
29
- declare function worstCaseArtifactBytes(html: string): number;
30
- declare function checkSmlCompatibility(input: EmailCompatibilityInput): readonly string[];
31
- //#endregion
32
- export { DEFAULT_EMAIL_TARGET_CLIENT_PROFILE, EmailClientDarkBehavior, EmailClientTarget, EmailCompatibilityInput, TargetClientProfile, checkDeliveryAssetDiagnostics, checkSmlCompatibility, collectSmlFeatureSlugs, isBreakingCompatibilityCheck, worstCaseArtifactBytes };
33
- //# sourceMappingURL=checks.d.ts.map
package/dist/checks.js DELETED
@@ -1,2 +0,0 @@
1
- import { a as isBreakingCompatibilityCheck, i as collectSmlFeatureSlugs, n as checkDeliveryAssetDiagnostics, o as worstCaseArtifactBytes, r as checkSmlCompatibility, t as DEFAULT_EMAIL_TARGET_CLIENT_PROFILE } from "./checks-D73Xizbw.js";
2
- export { DEFAULT_EMAIL_TARGET_CLIENT_PROFILE, checkDeliveryAssetDiagnostics, checkSmlCompatibility, collectSmlFeatureSlugs, isBreakingCompatibilityCheck, worstCaseArtifactBytes };
package/dist/classes.d.ts DELETED
@@ -1,26 +0,0 @@
1
- import { a as Span, t as Diagnostic } from "./diagnostics-OcQKQYSY.js";
2
- import { t as Declaration, u as UtilityScale } from "./registry-Dcp6QEK_.js";
3
- //#region src/classes.d.ts
4
- type ClassVariant = "base" | "sm" | "dark";
5
- interface ResolvedClass {
6
- readonly raw: string;
7
- readonly span: Span;
8
- readonly variant: ClassVariant;
9
- readonly declarations: ReadonlyArray<Declaration>;
10
- /**
11
- * Automatic dark-mode counterparts: present only on a base-variant
12
- * color-family class whose named token has a `colorsDark` entry. Literal
13
- * values and tokens without a dark pair never get auto-dark; explicit dark:
14
- * classes win by being emitted after these (CSS last-wins).
15
- */
16
- readonly darkDeclarations?: ReadonlyArray<Declaration> | undefined;
17
- }
18
- interface ClassResolution {
19
- readonly classes: ReadonlyArray<ResolvedClass>;
20
- readonly errors: ReadonlyArray<Diagnostic>;
21
- readonly warnings: ReadonlyArray<Diagnostic>;
22
- }
23
- declare const resolveClasses: (classAttribute: string, theme: UtilityScale, offset: number) => ClassResolution;
24
- //#endregion
25
- export { ClassResolution, ClassVariant, ResolvedClass, resolveClasses };
26
- //# sourceMappingURL=classes.d.ts.map
package/dist/classes.js DELETED
@@ -1,184 +0,0 @@
1
- import { parseCandidate, splitRoot } from "./candidate.js";
2
- import { forbiddenUtilities, registryRoots, utilityRegistry } from "./registry.js";
3
- //#region src/classes.ts
4
- const KNOWN_ROOT_SUMMARY = "supported utilities: p*/m* spacing, w/max-w/h, text-*, leading-*, tracking-*, font-*, bg-*, bg-gradient-to-* with from-*/to-*, border*, rounded*, uppercase, underline/no-underline, block/inline-block/hidden";
5
- const VARIANT_ERRORS = {
6
- hover: "email clients don't apply hover: styles — remove the variant",
7
- focus: "email clients don't apply focus: styles — remove the variant",
8
- active: "email clients don't apply active: styles — remove the variant",
9
- md: "only the sm: breakpoint is supported (progressive enhancement) — use sm:",
10
- lg: "only the sm: breakpoint is supported (progressive enhancement) — use sm:",
11
- xl: "only the sm: breakpoint is supported (progressive enhancement) — use sm:",
12
- "2xl": "only the sm: breakpoint is supported (progressive enhancement) — use sm:"
13
- };
14
- const REJECTED_ARBITRARY_FUNCTIONS = [
15
- ["var(", "CSS variables don't exist in compiled email — use a theme token or a literal value"],
16
- ["calc(", "calc() is unsupported in Outlook for Windows — use a literal px value"],
17
- ["oklch(", "oklch() is unsupported in email clients — use a literal hex color"],
18
- ["rgb(", "use a literal hex color — rgb()/rgba() alpha renders solid black in Outlook for Windows"],
19
- ["hsl(", "use a literal hex color — hsl() is unreliable across email clients"]
20
- ];
21
- const removeClassRepair = (raw, explanation) => ({
22
- kind: "remove-class",
23
- raw,
24
- suggestions: [],
25
- explanation
26
- });
27
- const replaceClassRepair = (raw, suggestions, explanation) => ({
28
- kind: "replace-class",
29
- raw,
30
- suggestions,
31
- explanation
32
- });
33
- const primitiveRepair = (raw, suggestions, explanation) => ({
34
- kind: "use-primitive",
35
- raw,
36
- suggestions,
37
- explanation
38
- });
39
- const variantRepair = (raw, variantName) => variantName === "md" || variantName === "lg" || variantName === "xl" || variantName === "2xl" ? replaceClassRepair(raw, [`sm:${raw.slice(raw.indexOf(":") + 1)}`, raw.slice(raw.indexOf(":") + 1)], "Only sm: exists for responsive email enhancement. Use sm: or remove the variant.") : replaceClassRepair(raw, [raw.slice(raw.indexOf(":") + 1)], "Email clients do not support this interaction/responsive variant. Remove the variant.");
40
- const forbiddenRepair = (raw) => {
41
- if (/^(inline-)?(flex|grid)$/.test(raw) || raw.startsWith("gap-")) return primitiveRepair(raw, ["<Row><Column>...</Column></Row>", "<Spacer height={16} />"], "Email layout must use table-backed primitives instead of flex/grid utilities.");
42
- if (raw.startsWith("items-") || raw.startsWith("justify-")) return primitiveRepair(raw, ["<Row><Column valign=\"middle\">...</Column></Row>", "text-center"], "Use Row/Column alignment or text alignment instead of flexbox alignment utilities.");
43
- if (raw.startsWith("overflow-") || raw === "truncate" || raw.startsWith("line-clamp-")) return primitiveRepair(raw, ["shorten the copy", "split content into rows/sections"], "Email clients cannot reliably clip or line-clamp content.");
44
- return removeClassRepair(raw, "This CSS feature is not reliable in email clients.");
45
- };
46
- const invalidValueRepair = (raw, root, value) => {
47
- if (root === "text" && /^\d+(\.\d+)?px$/.test(value)) return replaceClassRepair(raw, [
48
- "text-[15px]",
49
- "text-base",
50
- "text-lg",
51
- "text-xl",
52
- "text-2xl"
53
- ], "Arbitrary text sizes must be px values between 8px and 72px, or use a named text scale.");
54
- if (root.startsWith("rounded")) return replaceClassRepair(raw, [
55
- `${root}-md`,
56
- `${root}-lg`,
57
- `${root}-xl`,
58
- `${root}-[12px]`
59
- ], "Border radius values must use the supported radius scale, none/full, or a px arbitrary value.");
60
- };
61
- /**
62
- * Resolves one class attribute value. `offset` is the absolute source offset
63
- * of the attribute value's first character, so every class gets its own span.
64
- * Collects all errors instead of failing fast — agents fix a list at a time.
65
- */
66
- const ZERO_LENGTH = /^0(?:px|rem|em|%)?$/;
67
- const isZeroValued = (declarations) => declarations.every((declaration) => ZERO_LENGTH.test(declaration.value));
68
- const resolveClasses = (classAttribute, theme, offset) => {
69
- const classes = [];
70
- const errors = [];
71
- const warnings = [];
72
- for (const match of classAttribute.matchAll(/\S+/g)) {
73
- const raw = match[0];
74
- const span = {
75
- start: offset + match.index,
76
- end: offset + match.index + raw.length
77
- };
78
- const fail = (code, message, repair) => {
79
- errors.push(repair === void 0 ? {
80
- code,
81
- message,
82
- span
83
- } : {
84
- code,
85
- message,
86
- span,
87
- repair
88
- });
89
- };
90
- const candidate = parseCandidate(raw);
91
- if (candidate.important) {
92
- fail("unsupported-important", `remove the trailing ! from "${raw}" — the compiler manages !important for sm:/dark: rules`, removeClassRepair(raw, "The compiler manages specificity for sm:/dark: rules."));
93
- continue;
94
- }
95
- if (candidate.variants.length > 1) {
96
- fail("unsupported-variant", `at most one variant per class — split "${raw}" into separate sm:/dark: classes`, replaceClassRepair(raw, [candidate.base], "Use at most one variant per class; duplicate the base utility if both sm: and dark: variants are needed."));
97
- continue;
98
- }
99
- const variantName = candidate.variants[0];
100
- if (variantName !== void 0 && variantName !== "sm" && variantName !== "dark") {
101
- fail("unsupported-variant", `${variantName}: on "${raw}" — ${VARIANT_ERRORS[variantName] ?? "unknown variant — only sm: and dark: are supported"}`, variantRepair(raw, variantName));
102
- continue;
103
- }
104
- const variant = variantName ?? "base";
105
- const forbidden = forbiddenUtilities.find((entry) => entry.match.endsWith("-") ? candidate.base.startsWith(entry.match) : candidate.base === entry.match);
106
- if (forbidden !== void 0) {
107
- fail("forbidden-class", `class "${raw}" — ${forbidden.message}`, forbiddenRepair(raw));
108
- continue;
109
- }
110
- if (candidate.modifier !== null) {
111
- fail("unsupported-modifier", `the /${candidate.modifier} modifier on "${raw}" compiles to alpha colors, which render solid black in Outlook for Windows (Word engine) — use a solid color token`, replaceClassRepair(raw, [candidate.base], "Alpha modifiers are unsafe in Outlook for Windows. Use a solid token or literal hex color."));
112
- continue;
113
- }
114
- const split = splitRoot(candidate.base, registryRoots);
115
- if (split === null) {
116
- fail("unknown-class", `unknown class "${raw}" — ${KNOWN_ROOT_SUMMARY}`, removeClassRepair(raw, "Use only the closed SML email utility subset."));
117
- continue;
118
- }
119
- if (split.value?.kind === "arbitrary") {
120
- const arbitraryValue = split.value.value;
121
- const rejection = REJECTED_ARBITRARY_FUNCTIONS.find(([prefix]) => arbitraryValue.includes(prefix));
122
- if (rejection !== void 0) {
123
- fail("unsupported-arbitrary-value", `class "${raw}" — ${rejection[1]}`, replaceClassRepair(raw, split.root === "text" || split.root === "bg" || split.root === "border" ? [`${split.root}-[#374151]`] : [], "Use literal px lengths or literal hex colors only; functions and variables are not email-safe."));
124
- continue;
125
- }
126
- }
127
- const families = utilityRegistry.filter((family) => family.root === split.root);
128
- let resolved = null;
129
- for (const family of families) {
130
- let declarations = null;
131
- if (split.value === null) declarations = family.bare ?? null;
132
- else if (split.value.kind === "named") declarations = family.named?.(split.value.value, theme) ?? null;
133
- else declarations = family.arbitrary?.(split.value.value, theme) ?? null;
134
- if (declarations === null) continue;
135
- if (family.baseOnly === true && variantName !== void 0) {
136
- fail("unsupported-variant", `${variantName}: on "${raw}" — gradient utilities are combined per node at compile time and can't ride sm:/dark: head rules; remove the variant`, replaceClassRepair(raw, [candidate.base], "Gradient utilities compile per element, not as variant head rules."));
137
- resolved = null;
138
- break;
139
- }
140
- if (variant === "dark" && family.colorFamily !== true) {
141
- fail("dark-variant-non-color", `dark: applies only to color utilities (bg-*, text-{color}, border-{color}) — remove dark: from "${raw}"`, replaceClassRepair(raw, [candidate.base], "Only color utilities can have dark: variants in email."));
142
- resolved = null;
143
- break;
144
- }
145
- if (family.warn !== void 0 && !isZeroValued(declarations)) warnings.push({
146
- code: "warn-tier-class",
147
- message: `class "${raw}" — ${family.warn}`,
148
- span
149
- });
150
- const darkHex = variantName === void 0 && family.colorFamily === true && split.value?.kind === "named" ? theme.colorsDark[split.value.value] : void 0;
151
- resolved = darkHex === void 0 ? {
152
- raw,
153
- span,
154
- variant,
155
- declarations
156
- } : {
157
- raw,
158
- span,
159
- variant,
160
- declarations,
161
- darkDeclarations: declarations.map((declaration) => ({
162
- property: declaration.property,
163
- value: darkHex
164
- }))
165
- };
166
- break;
167
- }
168
- if (resolved !== null) {
169
- classes.push(resolved);
170
- continue;
171
- }
172
- if (errors.length > 0 && errors[errors.length - 1].span === span) continue;
173
- fail("unknown-class", `unknown class "${raw}" — ${split.value === null ? `"${split.root}" needs a value` : `"${split.value.value}" is not a valid value for ${split.root}-*`}`, split.value === null ? void 0 : invalidValueRepair(raw, split.root, split.value.value));
174
- }
175
- return {
176
- classes,
177
- errors,
178
- warnings
179
- };
180
- };
181
- //#endregion
182
- export { resolveClasses };
183
-
184
- //# sourceMappingURL=classes.js.map
@@ -1,28 +0,0 @@
1
- import { t as Diagnostic } from "./diagnostics-OcQKQYSY.js";
2
- import { f as ElementNode } from "./ast-BrDtApwS.js";
3
- import { a as SMS_TAG_SPECS } from "./parser-t29cHLVL.js";
4
- import { t as SmsEncoding } from "./sms-segments-y4FpPilJ.js";
5
- //#region src/compile-sms.d.ts
6
- /** Documented worst-case rendered width of one `{{variable}}`. */
7
- declare const VARIABLE_WORST_CASE_CHARS = 16;
8
- /** Derived from the grammar's closed category set (mirrors smsCategoryEnum). */
9
- type SmsCategory = (typeof SMS_TAG_SPECS)["Sms"]["attributes"]["category"]["values"][number];
10
- interface CompiledSms {
11
- readonly channel: "sms";
12
- /** The root tag's authoring-intent category; compliance state stays on variant rows. */
13
- readonly category?: SmsCategory | undefined;
14
- /** Template body: text verbatim, `{{vars}}` literal, `<If>` as sml: markers. */
15
- readonly body: string;
16
- readonly encoding: SmsEncoding;
17
- /** Segments of the primary rendering (every <If> true, variables at worst case). */
18
- readonly estimatedSegments: number;
19
- /** Worst branch combination; present only when the body has conditionals. */
20
- readonly maxSegments?: number | undefined;
21
- readonly warnings: ReadonlyArray<Diagnostic>;
22
- readonly compilerVersion: string;
23
- }
24
- /** Root is a parsed `<Sms>` element; the grammar already validated its shape. */
25
- declare const compileSmsRoot: (root: ElementNode) => CompiledSms;
26
- //#endregion
27
- export { CompiledSms, SmsCategory, VARIABLE_WORST_CASE_CHARS, compileSmsRoot };
28
- //# sourceMappingURL=compile-sms.d.ts.map
@@ -1,44 +0,0 @@
1
- import { stringAttribute } from "./ast.js";
2
- import { i as primaryChannelText, n as channelTextVariants, r as flattenChannelText, t as COMPILER_VERSION } from "./version-BITr58kk.js";
3
- import { hasSmsOptOut, smsSegmentInfo } from "./sms-segments.js";
4
- //#region src/compile-sms.ts
5
- /** Documented worst-case rendered width of one `{{variable}}`. */
6
- const VARIABLE_WORST_CASE_CHARS = 16;
7
- const WORST_CASE = { worstCaseChars: 16 };
8
- /** Root is a parsed `<Sms>` element; the grammar already validated its shape. */
9
- const compileSmsRoot = (root) => {
10
- const body = flattenChannelText(root.children);
11
- const category = stringAttribute(root, "category");
12
- const hasConditionals = root.children.some((child) => child.kind === "element" && child.tag === "If");
13
- const variantInfo = channelTextVariants(root.children, WORST_CASE).map(smsSegmentInfo);
14
- const encoding = variantInfo.some((info) => info.encoding === "ucs2") ? "ucs2" : "gsm7";
15
- const estimatedSegments = smsSegmentInfo(primaryChannelText(root.children, WORST_CASE)).segmentCount;
16
- const worstSegments = Math.max(estimatedSegments, ...variantInfo.map((info) => info.segmentCount));
17
- const warnings = [];
18
- const warn = (code, message) => {
19
- warnings.push({
20
- code,
21
- message,
22
- span: root.span
23
- });
24
- };
25
- if (encoding === "ucs2") warn("sms-ucs2", "sms uses non-GSM characters (UCS-2); each segment holds 70 characters instead of 160");
26
- if (worstSegments > 1) warn("sms-multi-segment", `sms spans ${worstSegments} segments (variables counted at 16 characters); each segment is billed separately`);
27
- if (worstSegments > 10) warn("sms-segment-limit", "sms exceeds 10 segments and may be rejected by carriers");
28
- const literalPrimary = primaryChannelText(root.children, "literal");
29
- if (literalPrimary.trim().length > 0 && !hasSmsOptOut(literalPrimary)) warn("sms-opt-out", "sms should include opt-out instructions (e.g. \"Reply STOP to unsubscribe\")");
30
- return {
31
- channel: "sms",
32
- category,
33
- body,
34
- encoding,
35
- estimatedSegments,
36
- maxSegments: hasConditionals ? worstSegments : void 0,
37
- warnings,
38
- compilerVersion: COMPILER_VERSION
39
- };
40
- };
41
- //#endregion
42
- export { VARIABLE_WORST_CASE_CHARS, compileSmsRoot };
43
-
44
- //# sourceMappingURL=compile-sms.js.map
@@ -1,56 +0,0 @@
1
- import { r as SmlCompileError, t as Diagnostic } from "./diagnostics-OcQKQYSY.js";
2
- import { f as ElementNode } from "./ast-BrDtApwS.js";
3
- import { l as WHATSAPP_TAG_SPECS } from "./parser-t29cHLVL.js";
4
- import { Result } from "better-result";
5
- //#region src/compile-whatsapp.d.ts
6
- type WhatsAppSpecs = typeof WHATSAPP_TAG_SPECS;
7
- /** Mirrors whatsappTemplateCategoryEnum, derived from the grammar's closed set. */
8
- type WhatsAppTemplateCategory = WhatsAppSpecs["WhatsApp"]["attributes"]["category"]["values"][number];
9
- /** Header type as authored (`type` attribute; defaults to "text"). */
10
- type WhatsAppHeaderType = WhatsAppSpecs["Header"]["attributes"]["type"]["values"][number];
11
- type WhatsAppHeaderMediaType = Exclude<WhatsAppHeaderType, "text">;
12
- /** Meta-wire button rows, stored as-is in the buttons jsonb column. */
13
- type WhatsAppButtonPayload = {
14
- readonly type: "QUICK_REPLY";
15
- readonly text: string;
16
- } | {
17
- readonly type: "URL";
18
- readonly text: string;
19
- readonly url: string;
20
- } | {
21
- readonly type: "PHONE_NUMBER";
22
- readonly text: string;
23
- readonly phone_number: string;
24
- } | {
25
- readonly type: "COPY_CODE";
26
- readonly example: string;
27
- };
28
- /** Variable names referenced per slot — the raw material for Meta example payloads. */
29
- interface WhatsAppExamples {
30
- readonly header?: ReadonlyArray<string> | undefined;
31
- readonly body?: ReadonlyArray<string> | undefined;
32
- readonly footer?: ReadonlyArray<string> | undefined;
33
- readonly buttons?: ReadonlyArray<string> | undefined;
34
- }
35
- interface CompiledWhatsApp {
36
- readonly channel: "whatsapp";
37
- /** Authoring-intent metadata; the API layer defaults name from the template slug. */
38
- readonly name?: string | undefined;
39
- /** The API layer defaults language from the variant locale ("en_US"). */
40
- readonly language?: string | undefined;
41
- readonly category: WhatsAppTemplateCategory;
42
- readonly headerType?: WhatsAppHeaderType | undefined;
43
- readonly headerMediaType?: WhatsAppHeaderMediaType | undefined;
44
- readonly headerContent?: string | undefined;
45
- readonly bodyContent: string;
46
- readonly footerContent?: string | undefined;
47
- readonly buttons: ReadonlyArray<WhatsAppButtonPayload>;
48
- readonly examples: WhatsAppExamples;
49
- readonly warnings: ReadonlyArray<Diagnostic>;
50
- readonly compilerVersion: string;
51
- }
52
- /** Root is a parsed `<WhatsApp>` element; the grammar already validated its anatomy. */
53
- declare const compileWhatsAppRoot: (root: ElementNode) => Result<CompiledWhatsApp, SmlCompileError>;
54
- //#endregion
55
- export { CompiledWhatsApp, WhatsAppButtonPayload, WhatsAppExamples, WhatsAppHeaderMediaType, WhatsAppHeaderType, WhatsAppTemplateCategory, compileWhatsAppRoot };
56
- //# sourceMappingURL=compile-whatsapp.d.ts.map
@@ -1,186 +0,0 @@
1
- import { SmlCompileError } from "./diagnostics.js";
2
- import { stringAttribute } from "./ast.js";
3
- import { n as channelTextVariants, r as flattenChannelText, t as COMPILER_VERSION } from "./version-BITr58kk.js";
4
- import { variableReferences } from "./interpolate.js";
5
- import { Result } from "better-result";
6
- //#region src/compile-whatsapp.ts
7
- const WHATSAPP_BUTTON_LIMITS = {
8
- URL: 2,
9
- PHONE_NUMBER: 1,
10
- QUICK_REPLY: 10,
11
- total: 10
12
- };
13
- const WHATSAPP_HEADER_MAX = 60;
14
- const WHATSAPP_FOOTER_MAX = 60;
15
- const WHATSAPP_BODY_MAX = 1024;
16
- const WHATSAPP_BUTTON_TEXT_MAX = 25;
17
- const POSITIONAL_PATTERN = /\{\{\s*(\d+)\s*\}\}/;
18
- const NAMED_PATTERN = /\{\{\s*([A-Za-z_][\w.]*)\s*\}\}/;
19
- const LITERAL = "literal";
20
- /** Longest possible send-time rendering of a slot, with literal placeholders. */
21
- const worstCaseLength = (node) => Math.max(0, ...channelTextVariants(node.children, LITERAL).map((variant) => variant.length));
22
- const component = (root, tag) => root.children.find((child) => child.kind === "element" && child.tag === tag);
23
- const slotExamples = (texts) => {
24
- const names = /* @__PURE__ */ new Set();
25
- for (const text of texts) for (const name of variableReferences(text)) names.add(name);
26
- return names.size === 0 ? void 0 : [...names].sort();
27
- };
28
- /** Root is a parsed `<WhatsApp>` element; the grammar already validated its anatomy. */
29
- const compileWhatsAppRoot = (root) => {
30
- const errors = [];
31
- const warnings = [];
32
- const header = component(root, "Header");
33
- const body = component(root, "Body");
34
- const footer = component(root, "Footer");
35
- const buttonsNode = component(root, "Buttons");
36
- const headerType = header === void 0 ? void 0 : stringAttribute(header, "type") ?? "text";
37
- const headerContent = header === void 0 ? void 0 : flattenChannelText(header.children);
38
- const bodyContent = flattenChannelText(body.children);
39
- const footerContent = footer === void 0 ? void 0 : flattenChannelText(footer.children);
40
- if (header !== void 0 && headerType === "text" && worstCaseLength(header) > WHATSAPP_HEADER_MAX) warnings.push({
41
- code: "whatsapp-header-length",
42
- message: `whatsapp header should stay under ${WHATSAPP_HEADER_MAX} characters`,
43
- span: header.span
44
- });
45
- if (worstCaseLength(body) > WHATSAPP_BODY_MAX) warnings.push({
46
- code: "whatsapp-body-length",
47
- message: `whatsapp body should stay under ${WHATSAPP_BODY_MAX} characters`,
48
- span: body.span
49
- });
50
- if (footer !== void 0 && worstCaseLength(footer) > WHATSAPP_FOOTER_MAX) warnings.push({
51
- code: "whatsapp-footer-length",
52
- message: `whatsapp footer should stay under ${WHATSAPP_FOOTER_MAX} characters`,
53
- span: footer.span
54
- });
55
- const buttonElements = buttonsNode === void 0 ? [] : buttonsNode.children.filter((child) => child.kind === "element" && child.tag === "Button");
56
- const buttons = [];
57
- for (const button of buttonElements) {
58
- const type = stringAttribute(button, "type");
59
- const text = flattenChannelText(button.children);
60
- const url = stringAttribute(button, "url");
61
- const phone = stringAttribute(button, "phone");
62
- if (url !== void 0 && type !== "url") {
63
- errors.push({
64
- code: "whatsapp-button-url",
65
- message: `url is only valid on <Button type="url"> — remove it or change the type`,
66
- span: button.span
67
- });
68
- continue;
69
- }
70
- if (phone !== void 0 && type !== "phone") {
71
- errors.push({
72
- code: "whatsapp-button-phone",
73
- message: `phone is only valid on <Button type="phone"> — remove it or change the type`,
74
- span: button.span
75
- });
76
- continue;
77
- }
78
- if (type !== "copy-code" && worstCaseLength(button) > WHATSAPP_BUTTON_TEXT_MAX) warnings.push({
79
- code: "whatsapp-button-text-length",
80
- message: `whatsapp button text should stay under ${WHATSAPP_BUTTON_TEXT_MAX} characters`,
81
- span: button.span
82
- });
83
- switch (type) {
84
- case "quick-reply":
85
- buttons.push({
86
- type: "QUICK_REPLY",
87
- text
88
- });
89
- break;
90
- case "url":
91
- if (url === void 0) {
92
- errors.push({
93
- code: "whatsapp-button-url",
94
- message: `<Button type="url"> requires a url attribute — add url="https://..."`,
95
- span: button.span
96
- });
97
- break;
98
- }
99
- buttons.push({
100
- type: "URL",
101
- text,
102
- url
103
- });
104
- break;
105
- case "phone":
106
- if (phone === void 0) {
107
- errors.push({
108
- code: "whatsapp-button-phone",
109
- message: `<Button type="phone"> requires a phone attribute — add phone="+15551234567"`,
110
- span: button.span
111
- });
112
- break;
113
- }
114
- buttons.push({
115
- type: "PHONE_NUMBER",
116
- text,
117
- phone_number: phone
118
- });
119
- break;
120
- case "copy-code": buttons.push({
121
- type: "COPY_CODE",
122
- example: text
123
- });
124
- }
125
- }
126
- const buttonsSpan = buttonsNode?.span ?? root.span;
127
- const countOf = (type) => buttons.filter((button) => button.type === type).length;
128
- if (countOf("URL") > WHATSAPP_BUTTON_LIMITS.URL) errors.push({
129
- code: "whatsapp-button-limit",
130
- message: `whatsapp allows at most ${WHATSAPP_BUTTON_LIMITS.URL} URL buttons`,
131
- span: buttonsSpan
132
- });
133
- if (countOf("PHONE_NUMBER") > WHATSAPP_BUTTON_LIMITS.PHONE_NUMBER) errors.push({
134
- code: "whatsapp-button-limit",
135
- message: `whatsapp allows at most ${WHATSAPP_BUTTON_LIMITS.PHONE_NUMBER} phone-number button`,
136
- span: buttonsSpan
137
- });
138
- if (countOf("QUICK_REPLY") > WHATSAPP_BUTTON_LIMITS.QUICK_REPLY) errors.push({
139
- code: "whatsapp-button-limit",
140
- message: `whatsapp allows at most ${WHATSAPP_BUTTON_LIMITS.QUICK_REPLY} quick-reply buttons`,
141
- span: buttonsSpan
142
- });
143
- if (buttonElements.length > WHATSAPP_BUTTON_LIMITS.total) errors.push({
144
- code: "whatsapp-button-limit",
145
- message: `whatsapp allows at most ${WHATSAPP_BUTTON_LIMITS.total} buttons total`,
146
- span: buttonsSpan
147
- });
148
- const placeholderSources = [
149
- headerContent ?? "",
150
- bodyContent,
151
- footerContent ?? "",
152
- ...buttons.map((button) => button.type === "URL" ? button.url : "")
153
- ];
154
- const hasPositional = placeholderSources.some((source) => POSITIONAL_PATTERN.test(source));
155
- const hasNamed = placeholderSources.some((source) => NAMED_PATTERN.test(source));
156
- if (hasPositional && hasNamed) warnings.push({
157
- code: "whatsapp-mixed-placeholders",
158
- message: "whatsapp mixes positional {{1}} and named {{var}} placeholders; Meta requires one style",
159
- span: root.span
160
- });
161
- if (errors.length > 0) return Result.err(new SmlCompileError({ diagnostics: errors }));
162
- return Result.ok({
163
- channel: "whatsapp",
164
- name: stringAttribute(root, "name"),
165
- language: stringAttribute(root, "language"),
166
- category: stringAttribute(root, "category"),
167
- headerType,
168
- headerMediaType: headerType === void 0 || headerType === "text" ? void 0 : headerType,
169
- headerContent,
170
- bodyContent,
171
- footerContent,
172
- buttons,
173
- examples: {
174
- header: slotExamples([headerContent ?? ""]),
175
- body: slotExamples([bodyContent]),
176
- footer: slotExamples([footerContent ?? ""]),
177
- buttons: slotExamples(buttons.map((button) => button.type === "URL" ? `${button.text} ${button.url}` : button.type === "COPY_CODE" ? button.example : button.text))
178
- },
179
- warnings,
180
- compilerVersion: COMPILER_VERSION
181
- });
182
- };
183
- //#endregion
184
- export { compileWhatsAppRoot };
185
-
186
- //# sourceMappingURL=compile-whatsapp.js.map
package/dist/compile.d.ts DELETED
@@ -1,18 +0,0 @@
1
- import { i as SmlParseError, r as SmlCompileError } from "./diagnostics-OcQKQYSY.js";
2
- import { b as SmlDocument } from "./ast-BrDtApwS.js";
3
- import { CompiledSms } from "./compile-sms.js";
4
- import { CompiledWhatsApp } from "./compile-whatsapp.js";
5
- import { Result } from "better-result";
6
- //#region src/version.d.ts
7
- declare const COMPILER_VERSION = "0.1.0";
8
- //#endregion
9
- //#region src/compile.d.ts
10
- /** Structured channel payloads compiled from recorded SML. */
11
- type CompiledTemplate = CompiledSms | CompiledWhatsApp;
12
- /** Compile a structured document; email definitions execute through the email runtime. */
13
- declare const compileSml: (document: SmlDocument) => Result<CompiledTemplate, SmlCompileError>;
14
- /** Parse and compile a structured SMS or WhatsApp document. */
15
- declare const compileSource: (source: string) => Result<CompiledTemplate, SmlParseError | SmlCompileError>;
16
- //#endregion
17
- export { COMPILER_VERSION, CompiledTemplate, compileSml, compileSource };
18
- //# sourceMappingURL=compile.d.ts.map
package/dist/compile.js DELETED
@@ -1,29 +0,0 @@
1
- import { SmlCompileError } from "./diagnostics.js";
2
- import { parseSml } from "./parser.js";
3
- import { t as COMPILER_VERSION } from "./version-BITr58kk.js";
4
- import { compileSmsRoot } from "./compile-sms.js";
5
- import { compileWhatsAppRoot } from "./compile-whatsapp.js";
6
- import { Result } from "better-result";
7
- //#region src/compile.ts
8
- /** Compile a structured document; email definitions execute through the email runtime. */
9
- const compileSml = (document) => {
10
- switch (document.root.tag) {
11
- case "Sms": return Result.ok(compileSmsRoot(document.root));
12
- case "WhatsApp": return compileWhatsAppRoot(document.root);
13
- default: return Result.err(new SmlCompileError({ diagnostics: [{
14
- code: "email-requires-object-template",
15
- message: "Email templates must default-export defineEmail with an input schema and fixtures.",
16
- span: document.root.span
17
- }] }));
18
- }
19
- };
20
- /** Parse and compile a structured SMS or WhatsApp document. */
21
- const compileSource = (source) => {
22
- const parsed = parseSml(source);
23
- if (Result.isError(parsed)) return Result.err(parsed.error);
24
- return compileSml(parsed.value);
25
- };
26
- //#endregion
27
- export { COMPILER_VERSION, compileSml, compileSource };
28
-
29
- //# sourceMappingURL=compile.js.map