@samva/markup 0.1.2 → 0.2.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 +25 -5
  2. package/README.md +92 -26
  3. package/dist/{ast-DAhnAvQN.d.ts → ast-63I-VLQx.d.ts} +15 -2
  4. package/dist/ast.d.ts +2 -2
  5. package/dist/ast.js +14 -1
  6. package/dist/{checks-D-zFQt5R.js → checks-DXuERO4B.js} +36 -2
  7. package/dist/checks.d.ts +8 -1
  8. package/dist/checks.js +2 -2
  9. package/dist/compile-sms.d.ts +2 -2
  10. package/dist/compile-whatsapp.d.ts +2 -2
  11. package/dist/compile.d.ts +9 -84
  12. package/dist/compile.js +11 -920
  13. package/dist/compiler.d.ts +301 -0
  14. package/dist/compiler.js +1389 -0
  15. package/dist/components.d.ts +3 -3
  16. package/dist/corpus-Dz5irJ9x.d.ts +161 -0
  17. package/dist/diagnostics-Djd9ckLg.d.ts +90 -0
  18. package/dist/diagnostics.js +4 -1
  19. package/dist/elements-DtwKlFVH.d.ts +820 -0
  20. package/dist/email/components.d.ts +119 -0
  21. package/dist/email/components.js +207 -0
  22. package/dist/email/diagnostics.d.ts +2 -0
  23. package/dist/email/diagnostics.js +56 -0
  24. package/dist/email/elements.d.ts +2 -0
  25. package/dist/email/elements.js +299 -0
  26. package/dist/email/html.d.ts +42 -0
  27. package/dist/email/html.js +266 -0
  28. package/dist/email/jsx-dev-runtime.d.ts +26 -0
  29. package/dist/email/jsx-dev-runtime.js +12 -0
  30. package/dist/email/jsx-runtime.d.ts +2 -0
  31. package/dist/email/jsx-runtime.js +60 -0
  32. package/dist/email/render.d.ts +36 -0
  33. package/dist/email/render.js +6 -0
  34. package/dist/email/selection.d.ts +102 -0
  35. package/dist/email/selection.js +143 -0
  36. package/dist/email/source-edit.d.ts +67 -0
  37. package/dist/email/source-edit.js +185 -0
  38. package/dist/email/stylesheets.d.ts +2 -0
  39. package/dist/email/stylesheets.js +43 -0
  40. package/dist/email/text.d.ts +7 -0
  41. package/dist/email/text.js +92 -0
  42. package/dist/emit.d.ts +13 -12
  43. package/dist/emit.js +63 -63
  44. package/dist/input-schema-BIfzI63B.d.ts +23 -0
  45. package/dist/input-schema.d.ts +2 -0
  46. package/dist/input-schema.js +170 -0
  47. package/dist/jsx-dev-runtime.d.ts +4 -3
  48. package/dist/jsx-dev-runtime.js +9 -1
  49. package/dist/{jsx-runtime-eNIMY87F.d.ts → jsx-runtime-C5k_Zq0A.d.ts} +3 -3
  50. package/dist/jsx-runtime.d.ts +1 -1
  51. package/dist/jsx-runtime.js +20 -1
  52. package/dist/model.d.ts +1 -1
  53. package/dist/ops.d.ts +1 -1
  54. package/dist/ops.js +1 -1
  55. package/dist/{parser-DkQKIjDa.d.ts → parser-CGBS73Gz.d.ts} +2 -2
  56. package/dist/parser.d.ts +1 -1
  57. package/dist/presets/category-mapping.d.ts +1 -1
  58. package/dist/presets/corpus.d.ts +2 -0
  59. package/dist/presets/corpus.js +186 -0
  60. package/dist/presets/public-gallery.d.ts +272 -2
  61. package/dist/presets/public-gallery.js +305 -6
  62. package/dist/presets/public-handoff.d.ts +140 -2
  63. package/dist/presets/public-handoff.js +130 -33
  64. package/dist/presets/public-preview-artifacts.d.ts +2 -2
  65. package/dist/presets/public-preview-artifacts.js +20 -26
  66. package/dist/presets/public-preview.d.ts +56 -2
  67. package/dist/presets/public-preview.js +388 -1152
  68. package/dist/presets/sources.d.ts +17 -4
  69. package/dist/presets/sources.js +1212 -43
  70. package/dist/presets.d.ts +2 -2
  71. package/dist/printer.d.ts +1 -1
  72. package/dist/render-Cddsy_XF.js +585 -0
  73. package/dist/render-template.d.ts +5 -2
  74. package/dist/render-template.js +3 -1
  75. package/dist/source-locations-BCJQnIfU.d.ts +89 -0
  76. package/dist/source-locations.d.ts +2 -0
  77. package/dist/source-locations.js +125 -0
  78. package/dist/stylesheets-oDRAnYA3.d.ts +94 -0
  79. package/dist/template-Dy5yOPyp.d.ts +60 -0
  80. package/dist/template.d.ts +2 -0
  81. package/dist/template.js +37 -0
  82. package/dist/tsx-source-instrumentation.d.ts +23 -0
  83. package/dist/tsx-source-instrumentation.js +97 -0
  84. package/dist/tsx-tracer.d.ts +116 -0
  85. package/dist/tsx-tracer.js +985 -0
  86. package/generated/sml-agent-contract.json +574 -782
  87. package/generated/sml-email-agent-contract.json +502 -677
  88. package/generated/sml-email-language-reference.md +94 -160
  89. package/generated/sml-language-reference.md +195 -220
  90. package/package.json +111 -9
  91. package/dist/public-gallery-D0Zv0pZE.d.ts +0 -611
  92. package/dist/public-preview-u1Z9aaGq.d.ts +0 -1168
@@ -0,0 +1,42 @@
1
+ import { r as EmailDiagnostic } from "../diagnostics-Djd9ckLg.js";
2
+ import { m as EmailNode } from "../elements-DtwKlFVH.js";
3
+ import { EmittedPosition } from "./selection.js";
4
+ //#region src/email/html.d.ts
5
+ /** Escape a value for element content. */
6
+ declare const escapeText: (value: string) => string;
7
+ /** Escape a value for a double-quoted attribute. */
8
+ declare const escapeAttribute: (value: string) => string;
9
+ /** camelCase to kebab-case; already-kebab names pass through unchanged. */
10
+ declare const kebab: (property: string) => string;
11
+ /** A bare number is pixels for every property that takes a length. */
12
+ declare const declarationValue: (property: string, value: string | number) => string;
13
+ interface EmailHtmlOutput {
14
+ readonly html: string;
15
+ readonly diagnostics: readonly EmailDiagnostic[];
16
+ /**
17
+ * Each emitted element's offset range and origins. A compatibility finding
18
+ * carries a position in the emitted document, and this is what turns that
19
+ * back into the authoring element the author can edit.
20
+ */
21
+ readonly positions: readonly EmittedPosition[];
22
+ }
23
+ /** How this document will be consumed. Delivery is the default; export is opt-in. */
24
+ interface SerializeEmailOptions {
25
+ /**
26
+ * Accept relative references. A local export serves assets from a relative
27
+ * base beside the file; a delivered message has no directory to resolve one
28
+ * against, so this stays off for anything that will be sent.
29
+ */
30
+ readonly relativeUrls?: boolean | undefined;
31
+ /**
32
+ * Stamp every element with its instance path, so an editor preview can
33
+ * resolve a click in the rendered document to exactly one rendered element.
34
+ * A delivered message never carries it.
35
+ */
36
+ readonly instancePaths?: boolean | undefined;
37
+ }
38
+ /** Lower the derived tree to email HTML, reporting every unsupported construct with its origin. */
39
+ declare const serializeEmailHtml: (node: EmailNode, options?: SerializeEmailOptions) => EmailHtmlOutput;
40
+ //#endregion
41
+ export { EmailHtmlOutput, SerializeEmailOptions, declarationValue, escapeAttribute, escapeText, kebab, serializeEmailHtml };
42
+ //# sourceMappingURL=html.d.ts.map
@@ -0,0 +1,266 @@
1
+ import { EMAIL_ELEMENTS, EMAIL_GLOBAL_ATTRIBUTES, emailElement } from "./elements.js";
2
+ import { INSTANCE_PATH_ATTRIBUTE } from "./selection.js";
3
+ import { Predicate } from "effect";
4
+ //#region src/email/html.ts
5
+ /** Escape a value for element content. */
6
+ const escapeText = (value) => value.replace(/[&<>]/g, (character) => ({
7
+ "&": "&amp;",
8
+ "<": "&lt;",
9
+ ">": "&gt;"
10
+ })[character] ?? character);
11
+ /** Escape a value for a double-quoted attribute. */
12
+ const escapeAttribute = (value) => value.replace(/[&<>"']/g, (character) => ({
13
+ "&": "&amp;",
14
+ "<": "&lt;",
15
+ ">": "&gt;",
16
+ "\"": "&quot;",
17
+ "'": "&#39;"
18
+ })[character] ?? character);
19
+ const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/;
20
+ const SAFE_URL_SCHEME = /^(https:\/\/|mailto:|tel:|cid:|#)/i;
21
+ const RELATIVE_URL = /^(\.{1,2}\/|\/(?!\/))/;
22
+ /**
23
+ * Only references an email client will follow safely; no `javascript:`, no
24
+ * `data:`. A relative reference is legal only for an export: a file on disk can
25
+ * resolve one against its own directory, and a delivered message cannot resolve
26
+ * anything but an absolute URL.
27
+ */
28
+ const isSafeUrl = (value, relative) => !/^[\u0000-\u0020]/.test(value) && !CONTROL_CHARACTERS.test(value) && !value.includes("\\") && (SAFE_URL_SCHEME.test(value) || relative && RELATIVE_URL.test(value));
29
+ const CSS_PROPERTY = /^(--[a-zA-Z0-9-]+|[a-zA-Z][a-zA-Z0-9-]*)$/;
30
+ const CSS_UNSAFE = /[;{}<>\\]|expression\s*\(|@import|\/\*/i;
31
+ const CSS_URL = /url\(\s*(['"]?)([^'")]*)\1\s*\)/gi;
32
+ /** camelCase to kebab-case; already-kebab names pass through unchanged. */
33
+ const kebab = (property) => property.startsWith("--") ? property : property.replace(/[A-Z]/g, (l) => `-${l.toLowerCase()}`);
34
+ const UNITLESS = /* @__PURE__ */ new Set([
35
+ "opacity",
36
+ "zIndex",
37
+ "fontWeight",
38
+ "lineHeight",
39
+ "order",
40
+ "flexGrow",
41
+ "flexShrink",
42
+ "columnCount",
43
+ "orphans",
44
+ "widows",
45
+ "zoom",
46
+ "fillOpacity",
47
+ "strokeOpacity"
48
+ ]);
49
+ /** A bare number is pixels for every property that takes a length. */
50
+ const declarationValue = (property, value) => typeof value === "number" && value !== 0 && !UNITLESS.has(property) && !property.startsWith("--") ? `${value}px` : String(value);
51
+ const report = (sink, code, message, origins, severity = "error") => {
52
+ sink.diagnostics.push({
53
+ code,
54
+ severity,
55
+ message,
56
+ origins
57
+ });
58
+ };
59
+ const declarations = (sink, style, origins) => {
60
+ const parts = [];
61
+ for (const [property, value] of Object.entries(style)) {
62
+ if (value === void 0 || value === null) continue;
63
+ if (!CSS_PROPERTY.test(property)) {
64
+ report(sink, "unsupported-style-property", `Style property ${JSON.stringify(property)} is not a CSS property name; write it in camelCase (backgroundColor) or as a custom property (--brand).`, origins);
65
+ continue;
66
+ }
67
+ if (typeof value !== "string" && typeof value !== "number") {
68
+ report(sink, "invalid-style-value", `Style ${property} must be a string or number, not ${typeof value}.`, origins);
69
+ continue;
70
+ }
71
+ const text = declarationValue(property, value);
72
+ if (CSS_UNSAFE.test(text) || CONTROL_CHARACTERS.test(text)) {
73
+ report(sink, "unsafe-style-value", `Style ${property} contains CSS that could break out of the declaration; remove ${JSON.stringify(text)}.`, origins);
74
+ continue;
75
+ }
76
+ let unsafeUrl;
77
+ for (const match of text.matchAll(CSS_URL)) if (!isSafeUrl(match[2] ?? "", sink.relativeUrls)) unsafeUrl = match[2] ?? "";
78
+ if (unsafeUrl !== void 0) {
79
+ report(sink, "unsafe-url", `Style ${property} points at ${JSON.stringify(unsafeUrl)}; email URLs must use https:, mailto:, tel:, cid: or a fragment${sink.relativeUrls ? ", or a relative path" : ""}.`, origins);
80
+ continue;
81
+ }
82
+ parts.push(`${kebab(property)}:${text}`);
83
+ }
84
+ return parts.join(";");
85
+ };
86
+ const attributeValue = (sink, tag, name, spec, value, origins) => {
87
+ if (value === void 0 || value === null || value === false) return void 0;
88
+ if (value === true) return "";
89
+ if (typeof value !== "string" && typeof value !== "number") {
90
+ report(sink, "invalid-attribute-value", `<${tag} ${name}> must be a string or number, not ${typeof value}.`, origins);
91
+ return;
92
+ }
93
+ const text = String(value);
94
+ if (CONTROL_CHARACTERS.test(text)) {
95
+ report(sink, "invalid-attribute-value", `<${tag} ${name}> contains control characters.`, origins);
96
+ return;
97
+ }
98
+ if (spec.values !== void 0 && !spec.values.includes(text)) {
99
+ report(sink, "invalid-attribute-value", `<${tag} ${name}> accepts ${spec.values.join(", ")}; received ${JSON.stringify(text)}.`, origins);
100
+ return;
101
+ }
102
+ if (spec.type === "number" && !/^-?\d+$/.test(text)) {
103
+ report(sink, "invalid-attribute-value", `<${tag} ${name}> must be an integer.`, origins);
104
+ return;
105
+ }
106
+ if (spec.type === "measure" && !/^\d+(\.\d+)?%?$/.test(text)) {
107
+ report(sink, "invalid-attribute-value", `<${tag} ${name}> must be a pixel count or a percentage.`, origins);
108
+ return;
109
+ }
110
+ if (spec.type === "url" && !isSafeUrl(text, sink.relativeUrls)) {
111
+ report(sink, "unsafe-url", sink.relativeUrls ? `<${tag} ${name}> must use https:, mailto:, tel:, cid:, a fragment, or a relative path; received ${JSON.stringify(text)}.` : `<${tag} ${name}> must use https:, mailto:, tel:, cid: or a fragment; received ${JSON.stringify(text)}. A relative path resolves only in an export, never in a delivered message.`, origins);
112
+ return;
113
+ }
114
+ return text;
115
+ };
116
+ const attributes = (sink, tag, spec, props, origins) => {
117
+ const emitted = [];
118
+ const present = /* @__PURE__ */ new Set();
119
+ let classes;
120
+ for (const [key, value] of Object.entries(props)) {
121
+ if (key === "key") continue;
122
+ const name = key === "className" ? "class" : key;
123
+ if (name === "class") {
124
+ if (value === void 0 || value === null) continue;
125
+ if (typeof value !== "string") {
126
+ report(sink, "invalid-attribute-value", `<${tag} class> must be a string.`, origins);
127
+ continue;
128
+ }
129
+ if (classes !== void 0) {
130
+ report(sink, "duplicate-attribute", `<${tag}> sets both class and className; keep one.`, origins);
131
+ continue;
132
+ }
133
+ classes = value;
134
+ emitted.push(` class="${escapeAttribute(value)}"`);
135
+ continue;
136
+ }
137
+ if (name === "style") {
138
+ if (value === void 0 || value === null) continue;
139
+ if (!Predicate.isObject(value) || Array.isArray(value)) {
140
+ report(sink, "invalid-style", `<${tag} style> must be a property object, for example style={{ backgroundColor: "#fff" }}.`, origins);
141
+ continue;
142
+ }
143
+ const css = declarations(sink, value, origins);
144
+ if (css !== "") emitted.push(` style="${escapeAttribute(css)}"`);
145
+ continue;
146
+ }
147
+ if (/^on/i.test(name)) {
148
+ report(sink, "event-handler", `<${tag} ${name}> is an event handler; email clients strip scripting and Samva refuses it.`, origins);
149
+ continue;
150
+ }
151
+ if (/^(data|aria)-[a-z0-9-]+$/.test(name)) {
152
+ const text = attributeValue(sink, tag, name, { type: "string" }, value, origins);
153
+ if (text !== void 0) emitted.push(` ${name}="${escapeAttribute(text)}"`);
154
+ continue;
155
+ }
156
+ const attribute = EMAIL_GLOBAL_ATTRIBUTES[name] ?? spec.attributes[name];
157
+ if (attribute === void 0) {
158
+ const supported = [
159
+ ...Object.keys(EMAIL_GLOBAL_ATTRIBUTES),
160
+ ...Object.keys(spec.attributes),
161
+ "class",
162
+ "style"
163
+ ].sort().join(", ");
164
+ report(sink, "unsupported-attribute", `<${tag}> does not support the ${name} attribute. Supported: ${supported}, plus data-* and aria-*.`, origins);
165
+ continue;
166
+ }
167
+ present.add(name);
168
+ const text = attributeValue(sink, tag, name, attribute, value, origins);
169
+ if (text !== void 0) emitted.push(` ${name}="${escapeAttribute(text)}"`);
170
+ }
171
+ for (const [name, attribute] of Object.entries(spec.attributes)) if (attribute.required === true && !present.has(name)) report(sink, "missing-attribute", `<${tag}> requires the ${name} attribute.`, origins);
172
+ return emitted.join("");
173
+ };
174
+ /** Fragments are transparent to a content model; raw compatibility markup is not content. */
175
+ const contentChildren = (children) => children.flatMap((child) => child._tag === "Fragment" ? contentChildren(child.children) : [child]);
176
+ const checkContentModel = (sink, tag, spec, children, origins) => {
177
+ if (spec.content === "void") {
178
+ if (contentChildren(children).length > 0) report(sink, "void-element-children", `<${tag}> cannot have children.`, origins);
179
+ return;
180
+ }
181
+ if (spec.content === "text" || spec.content === "raw") {
182
+ for (const child of contentChildren(children)) if (child._tag !== "Text") report(sink, "invalid-content", `<${tag}> accepts text only.`, child._tag === "Element" ? child.origins : origins);
183
+ return;
184
+ }
185
+ if (spec.childElements === void 0) return;
186
+ for (const child of contentChildren(children)) {
187
+ if (child._tag === "Raw") continue;
188
+ if (child._tag === "Text") {
189
+ if (child.value.trim() !== "") report(sink, "invalid-content", `<${tag}> cannot contain text directly; put it inside <${spec.childElements[0]}>.`, origins);
190
+ continue;
191
+ }
192
+ if (child._tag === "Element" && !spec.childElements.includes(child.tag)) report(sink, "invalid-content", `<${tag}> accepts only ${spec.childElements.map((name) => `<${name}>`).join(", ")}; found <${child.tag}>.`, child.origins);
193
+ }
194
+ };
195
+ /** Every write goes through here so `sink.written` stays the emitted offset. */
196
+ const emit = (sink, text) => {
197
+ sink.written += text.length;
198
+ return text;
199
+ };
200
+ const serialize = (sink, node, parentPath, counter) => {
201
+ switch (node._tag) {
202
+ case "Text": return emit(sink, escapeText(node.value));
203
+ case "Raw": return emit(sink, node.html);
204
+ case "Fragment": return node.children.map((child) => serialize(sink, child, parentPath, counter)).join("");
205
+ case "Element": {
206
+ const start = sink.written;
207
+ const index = counter.next++;
208
+ const path = parentPath === "" ? String(index) : `${parentPath}.${index}`;
209
+ const own = { next: 0 };
210
+ const stamp = sink.instancePaths ? ` ${INSTANCE_PATH_ATTRIBUTE}="${path}"` : "";
211
+ const spec = emailElement(node.tag);
212
+ if (spec === void 0) {
213
+ report(sink, "unsupported-element", `<${node.tag}> is not part of the supported email vocabulary. Supported elements: ${Object.keys(EMAIL_ELEMENTS).join(", ")}.`, node.origins);
214
+ return "";
215
+ }
216
+ checkContentModel(sink, node.tag, spec, node.children, node.origins);
217
+ if (spec.content === "raw") {
218
+ const css = node.children.map((child) => child._tag === "Text" ? child.value : "").join("");
219
+ if (/<\/|<!--/i.test(css)) {
220
+ report(sink, "unsafe-raw-text", `<${node.tag}> content cannot contain markup.`, node.origins);
221
+ return "";
222
+ }
223
+ const rawAttrs = attributes(sink, node.tag, spec, node.props, node.origins);
224
+ const raw = css === "" ? "" : `<${node.tag}${rawAttrs}${stamp}>${css}</${node.tag}>`;
225
+ return record(sink, start, node, path, emit(sink, raw));
226
+ }
227
+ const attrs = attributes(sink, node.tag, spec, node.props, node.origins);
228
+ if (spec.content === "void") return record(sink, start, node, path, emit(sink, `<${node.tag}${attrs}${stamp} />`));
229
+ const open = emit(sink, `<${node.tag}${attrs}${stamp}>`);
230
+ const children = node.children.map((child) => serialize(sink, child, path, own)).join("");
231
+ const close = emit(sink, `</${node.tag}>`);
232
+ return record(sink, start, node, path, `${open}${children}${close}`);
233
+ }
234
+ }
235
+ };
236
+ /** Record an element's emitted range once its content is written. */
237
+ const record = (sink, start, node, instancePath, text) => {
238
+ sink.positions.push({
239
+ start,
240
+ end: sink.written,
241
+ origins: node.origins,
242
+ instancePath,
243
+ tag: node.tag,
244
+ authored: node.authored
245
+ });
246
+ return text;
247
+ };
248
+ /** Lower the derived tree to email HTML, reporting every unsupported construct with its origin. */
249
+ const serializeEmailHtml = (node, options = {}) => {
250
+ const sink = {
251
+ diagnostics: [],
252
+ positions: [],
253
+ written: 0,
254
+ relativeUrls: options.relativeUrls === true,
255
+ instancePaths: options.instancePaths === true
256
+ };
257
+ return {
258
+ html: serialize(sink, node, "", { next: 0 }),
259
+ diagnostics: sink.diagnostics,
260
+ positions: sink.positions
261
+ };
262
+ };
263
+ //#endregion
264
+ export { declarationValue, escapeAttribute, escapeText, kebab, serializeEmailHtml };
265
+
266
+ //# sourceMappingURL=html.js.map
@@ -0,0 +1,26 @@
1
+ import { i as JsxSourceLocation, r as JsxSource } from "../source-locations-BCJQnIfU.js";
2
+ import { g as JSX, h as Fragment, m as EmailNode, p as EmailComponent } from "../elements-DtwKlFVH.js";
3
+ //#region src/email/jsx-dev-runtime.d.ts
4
+ /** Development JSX retains compiler-provided origins directly on the semantic tree. */
5
+ declare const jsxDEV: (type: string | EmailComponent<Record<string, unknown>> | typeof Fragment, props: Record<string, unknown> | null, _key?: unknown, _staticChildren?: boolean, source?: JsxSource) => {
6
+ readonly _tag: "Element";
7
+ readonly tag: string;
8
+ readonly props: Readonly<Record<string, unknown>>;
9
+ readonly children: readonly EmailNode[];
10
+ readonly origins: readonly JsxSourceLocation[];
11
+ readonly authored: boolean;
12
+ } | {
13
+ readonly _tag: "Fragment";
14
+ readonly children: readonly EmailNode[];
15
+ } | {
16
+ readonly _tag: "Raw";
17
+ readonly html: string;
18
+ readonly text: string;
19
+ readonly origins: readonly JsxSourceLocation[];
20
+ } | {
21
+ readonly _tag: "Text";
22
+ readonly value: string;
23
+ };
24
+ //#endregion
25
+ export { Fragment, type JSX, jsxDEV };
26
+ //# sourceMappingURL=jsx-dev-runtime.d.ts.map
@@ -0,0 +1,12 @@
1
+ import { JSX_SOURCE_LOCATION_PROP } from "../source-locations.js";
2
+ import { Fragment, jsx } from "./jsx-runtime.js";
3
+ //#region src/email/jsx-dev-runtime.ts
4
+ /** Development JSX retains compiler-provided origins directly on the semantic tree. */
5
+ const jsxDEV = (type, props, _key, _staticChildren, source) => jsx(type, {
6
+ ...props,
7
+ [JSX_SOURCE_LOCATION_PROP]: props?.["__samva_source_location"] ?? source
8
+ });
9
+ //#endregion
10
+ export { Fragment, jsxDEV };
11
+
12
+ //# sourceMappingURL=jsx-dev-runtime.js.map
@@ -0,0 +1,2 @@
1
+ import { C as reportEmailDiagnostic, S as rawMarkup, _ as currentEmailOrigins, b as jsxs, f as EmailChild, g as JSX, h as Fragment, m as EmailNode, p as EmailComponent, v as drainEmailDiagnostics, x as normalizeChildren, y as jsx } from "../elements-DtwKlFVH.js";
2
+ export { EmailChild, EmailComponent, EmailNode, Fragment, JSX, currentEmailOrigins, drainEmailDiagnostics, jsx, jsxs, normalizeChildren, rawMarkup, reportEmailDiagnostic };
@@ -0,0 +1,60 @@
1
+ import { JSX_SOURCE_LOCATION_PROP, completeJsxSourceLocation } from "../source-locations.js";
2
+ import { Data, Predicate } from "effect";
3
+ //#region src/email/jsx-runtime.ts
4
+ const EmailNode = Data.taggedEnum();
5
+ const Fragment = Symbol.for("@samva/markup/email/Fragment");
6
+ const callSites = [];
7
+ /** Call sites enclosing the element being built, innermost first. */
8
+ const currentEmailOrigins = () => [...callSites].reverse();
9
+ const reported = [];
10
+ /** Record a finding from inside a compiler primitive, located at the authoring call site. */
11
+ const reportEmailDiagnostic = (diagnostic) => {
12
+ reported.push({
13
+ ...diagnostic,
14
+ origins: currentEmailOrigins()
15
+ });
16
+ };
17
+ /** Take and clear the findings primitives reported during a render. */
18
+ const drainEmailDiagnostics = () => reported.splice(0);
19
+ /** Normalize actual values without interpolation or recording proxies. */
20
+ const normalizeChildren = (input) => {
21
+ if (input === null || input === void 0 || typeof input === "boolean") return [];
22
+ if (typeof input === "string") return [EmailNode.Text({ value: input })];
23
+ if (typeof input === "number" && Number.isFinite(input)) return [EmailNode.Text({ value: String(input) })];
24
+ if (Array.isArray(input)) return input.flatMap(normalizeChildren);
25
+ if (Predicate.isTagged(input, "Text") || Predicate.isTagged(input, "Element") || Predicate.isTagged(input, "Fragment") || Predicate.isTagged(input, "Raw")) return [input];
26
+ throw new TypeError("Email children must be synchronous text, finite numbers, elements, or arrays");
27
+ };
28
+ /** Compatibility markup emitted by a compiler primitive, with the text it stands for. */
29
+ const rawMarkup = (html, text = "") => EmailNode.Raw({
30
+ html,
31
+ text,
32
+ origins: currentEmailOrigins()
33
+ });
34
+ /** Automatic JSX factory for ordinary HTML and synchronous imported components. */
35
+ const jsx = (type, props) => {
36
+ const { children, [JSX_SOURCE_LOCATION_PROP]: source, ...attributes } = props ?? {};
37
+ const origin = completeJsxSourceLocation(source);
38
+ if (type === Fragment) return EmailNode.Fragment({ children: normalizeChildren(children) });
39
+ if (typeof type === "string") return EmailNode.Element({
40
+ tag: type,
41
+ props: attributes,
42
+ children: normalizeChildren(children),
43
+ origins: origin === void 0 ? currentEmailOrigins() : [origin, ...currentEmailOrigins()],
44
+ authored: origin !== void 0
45
+ });
46
+ if (origin !== void 0) callSites.push(origin);
47
+ try {
48
+ return EmailNode.Fragment({ children: normalizeChildren(type({
49
+ ...attributes,
50
+ children
51
+ })) });
52
+ } finally {
53
+ if (origin !== void 0) callSites.pop();
54
+ }
55
+ };
56
+ const jsxs = jsx;
57
+ //#endregion
58
+ export { EmailNode, Fragment, currentEmailOrigins, drainEmailDiagnostics, jsx, jsxs, normalizeChildren, rawMarkup, reportEmailDiagnostic };
59
+
60
+ //# sourceMappingURL=jsx-runtime.js.map
@@ -0,0 +1,36 @@
1
+ import { r as EmailDiagnostic } from "../diagnostics-Djd9ckLg.js";
2
+ import { m as EmailNode } from "../elements-DtwKlFVH.js";
3
+ import { EmittedPosition } from "./selection.js";
4
+ import { SerializeEmailOptions } from "./html.js";
5
+ import { r as TemplateDefinition } from "../template-Dy5yOPyp.js";
6
+ //#region src/email/render.d.ts
7
+ /** Classic Outlook lays a document out correctly only under a transitional doctype. */
8
+ declare const EMAIL_DOCTYPE = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
9
+ interface EmailCompileOutput {
10
+ readonly html: string;
11
+ readonly text: string;
12
+ /** Findings that did not block the compile; blocking findings throw. */
13
+ readonly diagnostics: readonly EmailDiagnostic[];
14
+ /**
15
+ * Each emitted element's offset range and origins. Compatibility checks run
16
+ * in the host over the emitted document, and this is what lets a finding name
17
+ * the authoring element rather than a character offset.
18
+ */
19
+ readonly positions: readonly EmittedPosition[];
20
+ }
21
+ /** Lower the derived tree to HTML and its plain-text alternative. */
22
+ declare const compileEmail: (node: EmailNode, options?: SerializeEmailOptions) => EmailCompileOutput;
23
+ interface RenderedEmail {
24
+ readonly subject: string;
25
+ readonly preheader: string | undefined;
26
+ readonly html: string;
27
+ readonly text: string;
28
+ readonly tree: EmailNode;
29
+ readonly diagnostics: readonly EmailDiagnostic[];
30
+ readonly positions: readonly EmittedPosition[];
31
+ }
32
+ /** Render one validated input through the template's only content function. Host isolation is required for untrusted source. */
33
+ declare const renderEmail: <Input>(template: TemplateDefinition<Input>, data: unknown, options?: SerializeEmailOptions) => RenderedEmail;
34
+ //#endregion
35
+ export { EMAIL_DOCTYPE, EmailCompileOutput, RenderedEmail, compileEmail, renderEmail };
36
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1,6 @@
1
+ import { n as compileEmail, r as renderEmail, t as EMAIL_DOCTYPE } from "../render-Cddsy_XF.js";
2
+ import "./html.js";
3
+ import "./diagnostics.js";
4
+ import "./stylesheets.js";
5
+ import "./text.js";
6
+ export { EMAIL_DOCTYPE, compileEmail, renderEmail };
@@ -0,0 +1,102 @@
1
+ import { i as JsxSourceLocation } from "../source-locations-BCJQnIfU.js";
2
+ import { Schema } from "effect";
3
+ //#region src/email/selection.d.ts
4
+ /**
5
+ * The attribute a preview stamps so a click in a rendered document resolves to
6
+ * exactly one rendered element. A delivered message never carries it.
7
+ */
8
+ declare const INSTANCE_PATH_ATTRIBUTE = "data-samva-instance";
9
+ /** Where one element sits in the emitted document, with the authoring that produced it. */
10
+ declare const EmittedPosition: Schema.Struct<{
11
+ readonly start: Schema.Int;
12
+ readonly end: Schema.Int;
13
+ readonly origins: Schema.$Array<Schema.Struct<{
14
+ readonly fileName: Schema.String;
15
+ readonly lineNumber: Schema.Int;
16
+ readonly columnNumber: Schema.Int;
17
+ }>>;
18
+ /**
19
+ * Dot-separated element indices from the document root, assigned as the
20
+ * document is written. It is the identity of one rendered instance.
21
+ */
22
+ readonly instancePath: Schema.String;
23
+ readonly tag: Schema.String;
24
+ /** False when a compiler primitive generated this element. */
25
+ readonly authored: Schema.Boolean;
26
+ }>;
27
+ interface EmittedPosition extends Schema.Schema.Type<typeof EmittedPosition> {}
28
+ /** One rendered element, and the authoring it came from. */
29
+ declare const EmailElementSelection: Schema.Struct<{
30
+ /**
31
+ * Dot-separated element indices from the document root. It is the identity
32
+ * of one rendered instance, which is what tells two iterations of the same
33
+ * authored element apart: their origins are identical.
34
+ */
35
+ readonly instancePath: Schema.String;
36
+ readonly tag: Schema.String;
37
+ /**
38
+ * The authored JSX element first, then each enclosing component call site,
39
+ * innermost first. Empty when the compiler generated this element with no
40
+ * authoring on the stack — the hidden preheader block, for instance.
41
+ */
42
+ readonly origins: Schema.$Array<Schema.Struct<{
43
+ readonly fileName: Schema.String;
44
+ readonly lineNumber: Schema.Int;
45
+ readonly columnNumber: Schema.Int;
46
+ }>>;
47
+ /**
48
+ * False when a compiler primitive generated this element. `origins[0]` is
49
+ * then the authoring element that asked for it rather than this element
50
+ * itself, so the wrapper selects the primitive the author wrote.
51
+ */
52
+ readonly authored: Schema.Boolean;
53
+ /** 1-based position among the rendered elements sharing this origin chain and tag. */
54
+ readonly occurrence: Schema.Int;
55
+ /**
56
+ * How many rendered elements share that chain and tag. Above one, one piece
57
+ * of authoring produced several rendered elements — a loop over a component,
58
+ * or a primitive that generates a repeated cell — so an edit there changes
59
+ * all of them.
60
+ */
61
+ readonly occurrences: Schema.Int;
62
+ /** Absent for the document root. */
63
+ readonly parentPath: Schema.optionalKey<Schema.String>;
64
+ /** Offset range in the rendered HTML this selection was taken from. */
65
+ readonly start: Schema.Int;
66
+ readonly end: Schema.Int;
67
+ }>;
68
+ interface EmailElementSelection extends Schema.Schema.Type<typeof EmailElementSelection> {}
69
+ /**
70
+ * Turn one render's emitted positions into selections, in document order.
71
+ *
72
+ * The serializer records an element once its content is written, so positions
73
+ * arrive innermost-first; document order is what an outline and an ancestry
74
+ * walk both need.
75
+ */
76
+ declare const emailSelections: (positions: readonly EmittedPosition[]) => readonly EmailElementSelection[];
77
+ /** Look selections up by the instance path a rendered document carries. */
78
+ declare const emailSelectionIndex: (selections: readonly EmailElementSelection[]) => ReadonlyMap<string, EmailElementSelection>;
79
+ /**
80
+ * The authoring element a selection edits. For an authored element that is the
81
+ * element itself; for a generated wrapper it is the primitive that asked for
82
+ * it. Undefined only where the compiler generated markup with no authoring on
83
+ * the stack, which nothing can edit in place.
84
+ */
85
+ declare const emailAuthoringTarget: (selection: EmailElementSelection) => JsxSourceLocation | undefined;
86
+ /** Root-first ancestry of a selection, itself last. */
87
+ declare const emailSelectionAncestry: (index: ReadonlyMap<string, EmailElementSelection>, instancePath: string) => readonly EmailElementSelection[];
88
+ /**
89
+ * Every rendered instance produced through the same call site, in document
90
+ * order. One entry means the call ran once; several mean it repeated.
91
+ */
92
+ declare const emailSelectionSiblings: (selections: readonly EmailElementSelection[], selection: EmailElementSelection) => readonly EmailElementSelection[];
93
+ /**
94
+ * Every rendered instance of the same authored element, across every call site
95
+ * that reaches it. A shared component edited at its definition changes all of
96
+ * these; edited at one call site it changes only `emailSelectionSiblings`. A
97
+ * control has to say which of the two it is doing before it applies.
98
+ */
99
+ declare const emailDefinitionInstances: (selections: readonly EmailElementSelection[], selection: EmailElementSelection) => readonly EmailElementSelection[];
100
+ //#endregion
101
+ export { EmailElementSelection, EmittedPosition, INSTANCE_PATH_ATTRIBUTE, emailAuthoringTarget, emailDefinitionInstances, emailSelectionAncestry, emailSelectionIndex, emailSelectionSiblings, emailSelections };
102
+ //# sourceMappingURL=selection.d.ts.map