@samva/markup 0.7.0 → 0.9.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 (176) hide show
  1. package/AGENTS.md +104 -37
  2. package/README.md +188 -52
  3. package/dist/assets-C5duwTya.d.ts +40 -0
  4. package/dist/brand-B-0bN_Ir.d.ts +88 -0
  5. package/dist/{render-aaJBnsXK.js → cascade-t6kxlgEI.js} +130 -160
  6. package/dist/channel-edit.d.ts +121 -0
  7. package/dist/channel-edit.js +609 -0
  8. package/dist/channel-specs-B6M_HlaV.js +29 -0
  9. package/dist/channel-specs-C2feJwuP.d.ts +13 -0
  10. package/dist/compatibility-DbISoFIq.js +589 -0
  11. package/dist/compiler.d.ts +19 -249
  12. package/dist/compiler.js +10 -1408
  13. package/dist/{css-COcFOT1y.d.ts → css-BVQKICKC.d.ts} +30 -4
  14. package/dist/diagnostic-codes.d.ts +403 -0
  15. package/dist/diagnostic-codes.js +399 -0
  16. package/dist/{diagnostics-BMYtBCQk.d.ts → diagnostics-B-Gym34R.d.ts} +8 -4
  17. package/dist/diagnostics.d.ts +31 -2
  18. package/dist/diagnostics.js +1 -19
  19. package/dist/{elements-BzjjnoCS.d.ts → elements-cqldpaJt.d.ts} +3 -3
  20. package/dist/email/assets.d.ts +2 -0
  21. package/dist/email/assets.js +63 -0
  22. package/dist/email/brand-footer.d.ts +34 -0
  23. package/dist/email/brand-footer.js +63 -0
  24. package/dist/email/brand.d.ts +2 -0
  25. package/dist/email/brand.js +148 -0
  26. package/dist/email/components.d.ts +21 -10
  27. package/dist/email/components.js +46 -11
  28. package/dist/email/diagnostics.d.ts +2 -2
  29. package/dist/email/diagnostics.js +2 -1
  30. package/dist/email/elements.d.ts +1 -1
  31. package/dist/email/html.d.ts +2 -42
  32. package/dist/email/html.js +56 -2
  33. package/dist/email/jsx-dev-runtime.d.ts +2 -2
  34. package/dist/email/jsx-runtime.d.ts +1 -1
  35. package/dist/email/render.d.ts +10 -13
  36. package/dist/email/render.js +163 -6
  37. package/dist/email/text.d.ts +1 -1
  38. package/dist/fmt.d.ts +32 -0
  39. package/dist/fmt.js +174 -0
  40. package/dist/html-BJepOwZ4.d.ts +56 -0
  41. package/dist/index.d.ts +2 -0
  42. package/dist/index.js +2 -0
  43. package/dist/ir-aaqNE987.d.ts +252 -0
  44. package/dist/ir.d.ts +2 -0
  45. package/dist/ir.js +7 -0
  46. package/dist/preview-Ekdg1dXG.d.ts +100 -0
  47. package/dist/preview.d.ts +2 -0
  48. package/dist/preview.js +160 -0
  49. package/dist/profile-BH-OtdDA.js +3670 -0
  50. package/dist/render-ir.d.ts +19 -0
  51. package/dist/render-ir.js +826 -0
  52. package/dist/sms-segments.d.ts +13 -2
  53. package/dist/sms.d.ts +16 -0
  54. package/dist/sms.js +12 -0
  55. package/dist/{email/source-edit.d.ts → source-edit.d.ts} +55 -6
  56. package/dist/source-edit.js +343 -0
  57. package/dist/source-locations-C8wCsiAy.d.ts +23 -0
  58. package/dist/source-locations.d.ts +2 -2
  59. package/dist/source-locations.js +3 -89
  60. package/dist/static.d.ts +57 -0
  61. package/dist/static.js +69 -0
  62. package/dist/tailwind-CMYpZf1v.js +614 -0
  63. package/dist/template-_QE_rQND.d.ts +65 -0
  64. package/dist/template.d.ts +2 -2
  65. package/dist/template.js +15 -10
  66. package/dist/theme-css-hFqcPSar.js +587 -0
  67. package/dist/theme-css.d.ts +57 -16
  68. package/dist/theme-css.js +2 -509
  69. package/dist/theme-imports-lvBw3AD1.js +109 -0
  70. package/dist/transform-Ct2OMkFN.d.ts +153 -0
  71. package/dist/unsubscribe-link-CoesnQDU.js +26 -0
  72. package/dist/whatsapp.d.ts +57 -0
  73. package/dist/whatsapp.js +22 -0
  74. package/generated/contract.json +1782 -0
  75. package/generated/reference.md +353 -0
  76. package/package.json +56 -154
  77. package/samva-brand.d.ts +14 -0
  78. package/skills/sml/SKILL.md +123 -0
  79. package/dist/ast-BrDtApwS.d.ts +0 -107
  80. package/dist/ast.d.ts +0 -2
  81. package/dist/ast.js +0 -124
  82. package/dist/candidate.d.ts +0 -29
  83. package/dist/candidate.js +0 -88
  84. package/dist/catalog-60eaYC7b.d.ts +0 -356
  85. package/dist/checks-DBweSIPN.js +0 -58954
  86. package/dist/checks.d.ts +0 -33
  87. package/dist/checks.js +0 -2
  88. package/dist/classes.d.ts +0 -27
  89. package/dist/classes.js +0 -184
  90. package/dist/compile-sms.d.ts +0 -28
  91. package/dist/compile-sms.js +0 -44
  92. package/dist/compile-whatsapp.d.ts +0 -56
  93. package/dist/compile-whatsapp.js +0 -186
  94. package/dist/compile.d.ts +0 -18
  95. package/dist/compile.js +0 -29
  96. package/dist/components.d.ts +0 -289
  97. package/dist/components.js +0 -106
  98. package/dist/conditionals.d.ts +0 -10
  99. package/dist/conditionals.js +0 -74
  100. package/dist/corpus-BjYMqOoY.d.ts +0 -106
  101. package/dist/diagnostics-OcQKQYSY.d.ts +0 -49
  102. package/dist/email/selection.d.ts +0 -91
  103. package/dist/email/selection.js +0 -91
  104. package/dist/email/source-edit.js +0 -185
  105. package/dist/email/stylesheets.d.ts +0 -15
  106. package/dist/email/stylesheets.js +0 -45
  107. package/dist/emit.d.ts +0 -51
  108. package/dist/emit.js +0 -151
  109. package/dist/footer.d.ts +0 -66
  110. package/dist/footer.js +0 -93
  111. package/dist/interpolate.d.ts +0 -24
  112. package/dist/interpolate.js +0 -43
  113. package/dist/jsx-dev-runtime.d.ts +0 -11
  114. package/dist/jsx-dev-runtime.js +0 -15
  115. package/dist/jsx-runtime-I-1nCtl2.d.ts +0 -57
  116. package/dist/jsx-runtime.d.ts +0 -2
  117. package/dist/jsx-runtime.js +0 -121
  118. package/dist/lexer.d.ts +0 -60
  119. package/dist/lexer.js +0 -281
  120. package/dist/model.d.ts +0 -24
  121. package/dist/model.js +0 -77
  122. package/dist/ops-2aiiYO7m.js +0 -630
  123. package/dist/ops.d.ts +0 -114
  124. package/dist/ops.js +0 -2
  125. package/dist/parser-t29cHLVL.d.ts +0 -396
  126. package/dist/parser.d.ts +0 -2
  127. package/dist/parser.js +0 -859
  128. package/dist/predicate-BI6uRRM-.js +0 -549
  129. package/dist/predicate-M-05Nd7R.d.ts +0 -56
  130. package/dist/predicate.d.ts +0 -2
  131. package/dist/predicate.js +0 -2
  132. package/dist/presets/category-mapping.d.ts +0 -13
  133. package/dist/presets/category-mapping.js +0 -49
  134. package/dist/presets/corpus.d.ts +0 -2
  135. package/dist/presets/corpus.js +0 -194
  136. package/dist/presets/public-gallery.d.ts +0 -266
  137. package/dist/presets/public-gallery.js +0 -916
  138. package/dist/presets/public-handoff.d.ts +0 -164
  139. package/dist/presets/public-handoff.js +0 -361
  140. package/dist/presets/public-preview-artifacts.d.ts +0 -43
  141. package/dist/presets/public-preview-artifacts.js +0 -104
  142. package/dist/presets/public-preview.d.ts +0 -61
  143. package/dist/presets/public-preview.js +0 -83
  144. package/dist/presets/showcase.d.ts +0 -2
  145. package/dist/presets/showcase.js +0 -627
  146. package/dist/presets/sources.d.ts +0 -38
  147. package/dist/presets/sources.js +0 -6083
  148. package/dist/presets.d.ts +0 -2
  149. package/dist/presets.js +0 -881
  150. package/dist/printer.d.ts +0 -6
  151. package/dist/printer.js +0 -122
  152. package/dist/registry.d.ts +0 -42
  153. package/dist/registry.js +0 -646
  154. package/dist/render-template.d.ts +0 -45
  155. package/dist/render-template.js +0 -76
  156. package/dist/showcase-DGZAWn4Z.d.ts +0 -456
  157. package/dist/sms-segments-y4FpPilJ.d.ts +0 -13
  158. package/dist/source-locations-DrCM82Ym.d.ts +0 -67
  159. package/dist/template-DWftqh4x.d.ts +0 -60
  160. package/dist/theme-6ApjjiBo.d.ts +0 -116
  161. package/dist/theme.d.ts +0 -2
  162. package/dist/theme.js +0 -118
  163. package/dist/tokens.d.ts +0 -6
  164. package/dist/tokens.js +0 -417
  165. package/dist/tsx-source-instrumentation.d.ts +0 -30
  166. package/dist/tsx-source-instrumentation.js +0 -115
  167. package/dist/tsx-tracer.d.ts +0 -112
  168. package/dist/tsx-tracer.js +0 -985
  169. package/dist/variables-J_L3qxRa.d.ts +0 -77
  170. package/dist/variables.d.ts +0 -2
  171. package/dist/variables.js +0 -141
  172. package/dist/version-BITr58kk.js +0 -96
  173. package/generated/sml-agent-contract.json +0 -709
  174. package/generated/sml-email-agent-contract.json +0 -615
  175. package/generated/sml-email-language-reference.md +0 -94
  176. package/generated/sml-language-reference.md +0 -195
@@ -1,6 +1,163 @@
1
- import { i as renderEmail, n as bindStylesheets, r as compileEmail, t as EMAIL_DOCTYPE } from "../render-aaJBnsXK.js";
2
- import "./html.js";
3
- import "./diagnostics.js";
4
- import "./stylesheets.js";
5
- import "./text.js";
6
- export { EMAIL_DOCTYPE, bindStylesheets, compileEmail, renderEmail };
1
+ import { EmailCompileError, hasBlockingDiagnostic } from "./diagnostics.js";
2
+ import { EmailNode, drainEmailDiagnostics } from "./jsx-runtime.js";
3
+ import { t as applyStylesheet } from "../cascade-t6kxlgEI.js";
4
+ import { serializeEmailHtml } from "./html.js";
5
+ import { preheaderNode } from "./components.js";
6
+ import { deriveEmailText } from "./text.js";
7
+ //#region src/email/render.ts
8
+ /** Classic Outlook lays a document out correctly only under a transitional doctype. */
9
+ const EMAIL_DOCTYPE = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
10
+ const settle = (diagnostics) => {
11
+ if (hasBlockingDiagnostic(diagnostics)) throw new EmailCompileError({ diagnostics });
12
+ return diagnostics;
13
+ };
14
+ /** Lower the derived tree to HTML and its plain-text alternative. */
15
+ const compileEmail = (node, options = {}) => {
16
+ const serialized = serializeEmailHtml(node, options);
17
+ const diagnostics = settle([...drainEmailDiagnostics(), ...serialized.diagnostics]);
18
+ return {
19
+ html: serialized.html,
20
+ text: deriveEmailText(node),
21
+ diagnostics,
22
+ positions: serialized.positions
23
+ };
24
+ };
25
+ /** Put the hidden preview block first inside `<body>`, or first overall when there is no shell. */
26
+ const withPreheader = (node, text) => {
27
+ const insert = (current) => {
28
+ if (current.type === "Fragment") {
29
+ for (const [index, child] of current.children.entries()) {
30
+ const replaced = insert(child);
31
+ if (replaced !== void 0) return EmailNode.Fragment({ children: current.children.with(index, replaced) });
32
+ }
33
+ return;
34
+ }
35
+ if (current.type !== "Element") return void 0;
36
+ if (current.tag === "body") return EmailNode.Element({
37
+ ...current,
38
+ children: [preheaderNode(text), ...current.children]
39
+ });
40
+ for (const [index, child] of current.children.entries()) {
41
+ const replaced = insert(child);
42
+ if (replaced !== void 0) return EmailNode.Element({
43
+ ...current,
44
+ children: current.children.with(index, replaced)
45
+ });
46
+ }
47
+ };
48
+ return insert(node) ?? EmailNode.Fragment({ children: [preheaderNode(text), node] });
49
+ };
50
+ const headStyle = (css, origins) => EmailNode.Element({
51
+ tag: "style",
52
+ props: {},
53
+ children: [EmailNode.Text({ value: css })],
54
+ origins,
55
+ authored: false
56
+ });
57
+ /**
58
+ * Put the retained rules in a `<style>` at the end of `<head>`, and project
59
+ * `@font-face` rules in a sibling classic Outlook cannot see. Outlook for
60
+ * Windows registers a face it cannot load and then renders the family in Times
61
+ * New Roman, ignoring the stack; hidden, the family is merely missing and the
62
+ * stack's fallback applies.
63
+ */
64
+ const withHeadCss = (node, css, fontFaceCss) => {
65
+ const insert = (current) => {
66
+ if (current.type === "Fragment") {
67
+ for (const [index, child] of current.children.entries()) {
68
+ const replaced = insert(child);
69
+ if (replaced !== void 0) return EmailNode.Fragment({ children: current.children.with(index, replaced) });
70
+ }
71
+ return;
72
+ }
73
+ if (current.type !== "Element") return void 0;
74
+ if (current.tag === "head") return EmailNode.Element({
75
+ ...current,
76
+ children: [
77
+ ...current.children,
78
+ ...css === "" ? [] : [headStyle(css, current.origins)],
79
+ ...fontFaceCss === "" ? [] : [
80
+ EmailNode.Raw({
81
+ html: "<!--[if !mso]><!-->",
82
+ text: "",
83
+ origins: []
84
+ }),
85
+ headStyle(fontFaceCss, current.origins),
86
+ EmailNode.Raw({
87
+ html: "<!--<![endif]-->",
88
+ text: "",
89
+ origins: []
90
+ })
91
+ ]
92
+ ]
93
+ });
94
+ for (const [index, child] of current.children.entries()) {
95
+ const replaced = insert(child);
96
+ if (replaced !== void 0) return EmailNode.Element({
97
+ ...current,
98
+ children: current.children.with(index, replaced)
99
+ });
100
+ }
101
+ };
102
+ return insert(node);
103
+ };
104
+ /**
105
+ * Render authored content against a stylesheet: inline what a client honors,
106
+ * keep the rest for `<head>`, and lower to HTML and text.
107
+ */
108
+ const renderEmailContent = (stylesheet, render, options = {}) => {
109
+ drainEmailDiagnostics();
110
+ const content = render();
111
+ const invalid = [];
112
+ const fail = (code, message) => invalid.push({
113
+ code,
114
+ severity: "error",
115
+ message,
116
+ origins: []
117
+ });
118
+ if (typeof content.subject !== "string" || /[\r\n\u0000]/.test(content.subject)) fail("invalid-subject", "Invalid email subject: it must be one line of text.");
119
+ if (content.preheader !== void 0 && typeof content.preheader !== "string") fail("invalid-preheader", "Invalid email preheader: it must be a string.");
120
+ if (content.text !== void 0 && typeof content.text !== "string") fail("invalid-text", "Invalid plain text: it must be a string.");
121
+ settle(invalid);
122
+ const base = content.preheader === void 0 ? content.body : withPreheader(content.body, content.preheader);
123
+ const styled = stylesheet === void 0 ? {
124
+ tree: base,
125
+ headCss: "",
126
+ fontFaceCss: "",
127
+ diagnostics: []
128
+ } : applyStylesheet(base, stylesheet);
129
+ const style = [...styled.diagnostics];
130
+ let tree = styled.tree;
131
+ if (styled.headCss !== "" || styled.fontFaceCss !== "") {
132
+ const placed = withHeadCss(tree, styled.headCss, styled.fontFaceCss);
133
+ if (placed === void 0) style.push({
134
+ code: "missing-head",
135
+ severity: "error",
136
+ message: "Styles that cannot be inlined — media queries, dark-scheme rules and pseudo-selectors — need a <head> to live in. Wrap the message in <Email>, or write those declarations as inline styles.",
137
+ origins: []
138
+ });
139
+ else tree = placed;
140
+ }
141
+ settle(style);
142
+ const compiled = compileEmail(tree, options);
143
+ const prefix = compiled.html.startsWith("<html") ? EMAIL_DOCTYPE : "";
144
+ const html = `${prefix}${compiled.html}`;
145
+ const positions = prefix === "" ? compiled.positions : compiled.positions.map((position) => ({
146
+ ...position,
147
+ start: position.start + prefix.length,
148
+ end: position.end + prefix.length
149
+ }));
150
+ return {
151
+ subject: content.subject,
152
+ preheader: content.preheader,
153
+ html,
154
+ text: content.text ?? compiled.text,
155
+ tree,
156
+ diagnostics: [...style, ...compiled.diagnostics],
157
+ positions
158
+ };
159
+ };
160
+ //#endregion
161
+ export { EMAIL_DOCTYPE, compileEmail, renderEmailContent };
162
+
163
+ //# sourceMappingURL=render.js.map
@@ -1,4 +1,4 @@
1
- import { m as EmailNode } from "../elements-BzjjnoCS.js";
1
+ import { m as EmailNode } from "../elements-cqldpaJt.js";
2
2
  //#region src/email/text.d.ts
3
3
  /** Derive the plain-text alternative from the semantic tree in reading order. */
4
4
  declare const deriveEmailText: (node: EmailNode) => string;
package/dist/fmt.d.ts ADDED
@@ -0,0 +1,32 @@
1
+ //#region src/fmt.d.ts
2
+ declare const DEFAULT_LOCALE = "en-US";
3
+ declare const DEFAULT_TIME_ZONE = "UTC";
4
+ interface FormatContext {
5
+ readonly locale?: string | undefined;
6
+ readonly timeZone?: string | undefined;
7
+ }
8
+ type DateStyle = "short" | "medium" | "long" | "full";
9
+ interface PluralForms {
10
+ readonly zero?: string;
11
+ readonly one?: string;
12
+ readonly two?: string;
13
+ readonly few?: string;
14
+ readonly many?: string;
15
+ readonly other: string;
16
+ }
17
+ interface Fmt {
18
+ money(amount: number, currency: string, locale?: string): string;
19
+ number(value: number, options?: Intl.NumberFormatOptions, locale?: string): string;
20
+ date(iso: string, style?: DateStyle, locale?: string): string;
21
+ time(iso: string, style?: DateStyle, locale?: string): string;
22
+ plural(count: number, forms: PluralForms, locale?: string): string;
23
+ list(values: readonly string[], locale?: string): string;
24
+ }
25
+ declare const FORMATTER_NAMES: readonly ["money", "number", "date", "time", "plural", "list"];
26
+ declare const createFmt: (context?: FormatContext) => Fmt;
27
+ declare const withFormatContext: <T>(context: FormatContext, run: () => T) => T;
28
+ declare const fmt: Fmt;
29
+ declare const callFormatter: (formatter: Fmt, name: string, args: readonly unknown[]) => string;
30
+ //#endregion
31
+ export { DEFAULT_LOCALE, DEFAULT_TIME_ZONE, DateStyle, FORMATTER_NAMES, Fmt, FormatContext, PluralForms, callFormatter, createFmt, fmt, withFormatContext };
32
+ //# sourceMappingURL=fmt.d.ts.map
package/dist/fmt.js ADDED
@@ -0,0 +1,174 @@
1
+ //#region src/fmt.ts
2
+ const DEFAULT_LOCALE = "en-US";
3
+ const DEFAULT_TIME_ZONE = "UTC";
4
+ const FORMATTER_NAMES = [
5
+ "money",
6
+ "number",
7
+ "date",
8
+ "time",
9
+ "plural",
10
+ "list"
11
+ ];
12
+ const CACHE_LIMIT = 200;
13
+ const cache = /* @__PURE__ */ new Map();
14
+ const cached = (kind, locale, options, make) => {
15
+ const key = `${kind}\u0000${locale}\u0000${options === void 0 ? "" : JSON.stringify(options)}`;
16
+ const hit = cache.get(key);
17
+ if (hit !== void 0) return hit;
18
+ const value = make();
19
+ if (cache.size >= CACHE_LIMIT) cache.clear();
20
+ cache.set(key, value);
21
+ return value;
22
+ };
23
+ const assertFinite = (value, what) => {
24
+ if (typeof value !== "number" || !Number.isFinite(value)) throw new TypeError(`${what} must be a finite number.`);
25
+ };
26
+ const DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
27
+ const parseIso = (iso) => {
28
+ if (typeof iso !== "string") throw new TypeError("ISO date must be a string.");
29
+ const date = new Date(DATE_ONLY.test(iso) ? `${iso}T00:00:00Z` : iso);
30
+ if (Number.isNaN(date.getTime())) throw new RangeError(`Cannot parse "${iso}" as an ISO 8601 date.`);
31
+ return date;
32
+ };
33
+ const createFmt = (context = {}) => {
34
+ const defaultLocale = context.locale ?? "en-US";
35
+ const timeZone = context.timeZone ?? "UTC";
36
+ const pick = (locale) => locale ?? defaultLocale;
37
+ return {
38
+ money: (amount, currency, locale) => {
39
+ assertFinite(amount, "money amount");
40
+ const l = pick(locale);
41
+ return cached("money", l, currency, () => new Intl.NumberFormat(l, {
42
+ style: "currency",
43
+ currency
44
+ })).format(amount);
45
+ },
46
+ number: (value, options, locale) => {
47
+ assertFinite(value, "number value");
48
+ const l = pick(locale);
49
+ return cached("number", l, options, () => new Intl.NumberFormat(l, options)).format(value);
50
+ },
51
+ date: (iso, style = "medium", locale) => {
52
+ const d = parseIso(iso);
53
+ const l = pick(locale);
54
+ return cached("date", l, {
55
+ style,
56
+ timeZone
57
+ }, () => new Intl.DateTimeFormat(l, {
58
+ dateStyle: style,
59
+ timeZone
60
+ })).format(d);
61
+ },
62
+ time: (iso, style = "short", locale) => {
63
+ const d = parseIso(iso);
64
+ const l = pick(locale);
65
+ return cached("time", l, {
66
+ style,
67
+ timeZone
68
+ }, () => new Intl.DateTimeFormat(l, {
69
+ timeStyle: style,
70
+ timeZone
71
+ })).format(d);
72
+ },
73
+ plural: (count, forms, locale) => {
74
+ assertFinite(count, "plural count");
75
+ const l = pick(locale);
76
+ return forms[cached("plural", l, void 0, () => new Intl.PluralRules(l)).select(count)] ?? forms.other;
77
+ },
78
+ list: (values, locale) => {
79
+ const l = pick(locale);
80
+ return cached("list", l, void 0, () => new Intl.ListFormat(l, {
81
+ type: "conjunction",
82
+ style: "long"
83
+ })).format(values);
84
+ }
85
+ };
86
+ };
87
+ let ambient = {};
88
+ const withFormatContext = (context, run) => {
89
+ const previous = ambient;
90
+ ambient = context;
91
+ try {
92
+ return run();
93
+ } finally {
94
+ ambient = previous;
95
+ }
96
+ };
97
+ const fmt = {
98
+ money: (a, c, l) => createFmt(ambient).money(a, c, l),
99
+ number: (v, o, l) => createFmt(ambient).number(v, o, l),
100
+ date: (i, s, l) => createFmt(ambient).date(i, s, l),
101
+ time: (i, s, l) => createFmt(ambient).time(i, s, l),
102
+ plural: (c, f, l) => createFmt(ambient).plural(c, f, l),
103
+ list: (v, l) => createFmt(ambient).list(v, l)
104
+ };
105
+ const STYLES = [
106
+ "short",
107
+ "medium",
108
+ "long",
109
+ "full"
110
+ ];
111
+ const PLURAL_KEYS = [
112
+ "zero",
113
+ "one",
114
+ "two",
115
+ "few",
116
+ "many",
117
+ "other"
118
+ ];
119
+ const arity = (name, args, min, max) => {
120
+ if (args.length < min || args.length > max) {
121
+ const expected = min === max ? `${min}` : `${min} to ${max}`;
122
+ throw new TypeError(`${name} takes ${expected} arguments but received ${args.length}.`);
123
+ }
124
+ };
125
+ const str = (name, index, value, optional = false) => {
126
+ if (value === void 0 && optional) return void 0;
127
+ if (typeof value !== "string") throw new TypeError(`${name} argument ${index + 1} must be a string.`);
128
+ return value;
129
+ };
130
+ const num = (name, index, value) => {
131
+ if (typeof value !== "number" || !Number.isFinite(value)) throw new TypeError(`${name} argument ${index + 1} must be a finite number.`);
132
+ return value;
133
+ };
134
+ const style = (name, index, value) => {
135
+ if (value === void 0) return void 0;
136
+ if (typeof value !== "string" || !STYLES.includes(value)) throw new TypeError(`${name} argument ${index + 1} must be one of ${STYLES.join(", ")}.`);
137
+ return value;
138
+ };
139
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
140
+ const callFormatter = (formatter, name, args) => {
141
+ switch (name) {
142
+ case "money":
143
+ arity(name, args, 2, 3);
144
+ return formatter.money(num(name, 0, args[0]), str(name, 1, args[1]), str(name, 2, args[2], true));
145
+ case "number": {
146
+ arity(name, args, 1, 3);
147
+ const options = args[1];
148
+ if (options !== void 0 && !isRecord(options)) throw new TypeError("number argument 2 must be an options object.");
149
+ return formatter.number(num(name, 0, args[0]), options, str(name, 2, args[2], true));
150
+ }
151
+ case "date":
152
+ case "time":
153
+ arity(name, args, 1, 3);
154
+ return formatter[name](str(name, 0, args[0]), style(name, 1, args[1]), str(name, 2, args[2], true));
155
+ case "plural": {
156
+ arity(name, args, 2, 3);
157
+ const forms = args[1];
158
+ if (!isRecord(forms) || typeof forms.other !== "string") throw new TypeError("plural argument 2 must be an object with a string `other` form.");
159
+ for (const key of PLURAL_KEYS) if (forms[key] !== void 0 && typeof forms[key] !== "string") throw new TypeError(`plural argument 2 form \`${key}\` must be a string.`);
160
+ return formatter.plural(num(name, 0, args[0]), forms, str(name, 2, args[2], true));
161
+ }
162
+ case "list": {
163
+ arity(name, args, 1, 2);
164
+ const values = args[0];
165
+ if (!Array.isArray(values) || values.some((v) => typeof v !== "string")) throw new TypeError("list argument 1 must be an array of strings.");
166
+ return formatter.list(values, str(name, 1, args[1], true));
167
+ }
168
+ default: throw new TypeError(`Unknown formatter "${name}".`);
169
+ }
170
+ };
171
+ //#endregion
172
+ export { DEFAULT_LOCALE, DEFAULT_TIME_ZONE, FORMATTER_NAMES, callFormatter, createFmt, fmt, withFormatContext };
173
+
174
+ //# sourceMappingURL=fmt.js.map
@@ -0,0 +1,56 @@
1
+ import { r as JsxSourceLocation } from "./source-locations-C8wCsiAy.js";
2
+ import { r as EmailDiagnostic } from "./diagnostics-B-Gym34R.js";
3
+ import { m as EmailNode } from "./elements-cqldpaJt.js";
4
+ import { n as EmittedPosition } from "./preview-Ekdg1dXG.js";
5
+ //#region src/email/html.d.ts
6
+ /** Escape a value for element content. */
7
+ declare const escapeText: (value: string) => string;
8
+ /** Escape a value for a double-quoted attribute. */
9
+ declare const escapeAttribute: (value: string) => string;
10
+ /** camelCase to kebab-case; already-kebab names pass through unchanged. */
11
+ declare const kebab: (property: string) => string;
12
+ /** A bare number is pixels for every property that takes a length. */
13
+ declare const declarationValue: (property: string, value: string | number) => string;
14
+ interface EmailHtmlOutput {
15
+ readonly html: string;
16
+ readonly diagnostics: readonly EmailDiagnostic[];
17
+ /**
18
+ * Each emitted element's offset range and origins. A compatibility finding
19
+ * carries a position in the emitted document, and this is what turns that
20
+ * back into the authoring element the author can edit.
21
+ */
22
+ readonly positions: readonly EmittedPosition[];
23
+ }
24
+ /** How this document will be consumed. Delivery is the default; export is opt-in. */
25
+ interface SerializeEmailOptions {
26
+ /**
27
+ * Accept relative references. A local export serves assets from a relative
28
+ * base beside the file; a delivered message has no directory to resolve one
29
+ * against, so this stays off for anything that will be sent.
30
+ */
31
+ readonly relativeUrls?: boolean | undefined;
32
+ /**
33
+ * Stamp every element with its instance path, so an editor preview can
34
+ * resolve a click in the rendered document to exactly one rendered element.
35
+ * A delivered message never carries it.
36
+ */
37
+ readonly instancePaths?: boolean | undefined;
38
+ }
39
+ /** Lower the derived tree to email HTML, reporting every unsupported construct with its origin. */
40
+ declare const serializeEmailHtml: (node: EmailNode, options?: SerializeEmailOptions) => EmailHtmlOutput;
41
+ /** An authored style object as a declaration list, checked by the same rules as a serialized tree. */
42
+ declare const serializeStyle: (style: Readonly<Record<string, unknown>>, origins: readonly JsxSourceLocation[]) => {
43
+ readonly css: string;
44
+ readonly diagnostics: readonly EmailDiagnostic[];
45
+ };
46
+ /**
47
+ * Check one static attribute value against its element's spec, as serialization does. Returns
48
+ * the text that would be emitted, or `undefined` when the attribute is dropped or invalid.
49
+ */
50
+ declare const checkAttribute: (tag: string, name: string, value: unknown, origins: readonly JsxSourceLocation[]) => {
51
+ readonly text: string | undefined;
52
+ readonly diagnostics: readonly EmailDiagnostic[];
53
+ };
54
+ //#endregion
55
+ export { escapeAttribute as a, serializeEmailHtml as c, declarationValue as i, serializeStyle as l, SerializeEmailOptions as n, escapeText as o, checkAttribute as r, kebab as s, EmailHtmlOutput as t };
56
+ //# sourceMappingURL=html-BJepOwZ4.d.ts.map
@@ -0,0 +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";
2
+ export { type EmailChannel, type SmlTemplate, TEMPLATE_ID_PATTERN, type TemplateInput, defineTemplate };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import { TEMPLATE_ID_PATTERN, defineTemplate } from "./template.js";
2
+ export { TEMPLATE_ID_PATTERN, defineTemplate };