@samva/markup 0.1.2 → 0.3.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 (93) 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 +303 -0
  14. package/dist/compiler.js +1390 -0
  15. package/dist/components.d.ts +3 -3
  16. package/dist/corpus-qrUD5O6r.d.ts +254 -0
  17. package/dist/css-ar2lU3NC.d.ts +84 -0
  18. package/dist/diagnostics-Djd9ckLg.d.ts +90 -0
  19. package/dist/diagnostics.js +4 -1
  20. package/dist/elements-DtwKlFVH.d.ts +820 -0
  21. package/dist/email/components.d.ts +125 -0
  22. package/dist/email/components.js +210 -0
  23. package/dist/email/diagnostics.d.ts +2 -0
  24. package/dist/email/diagnostics.js +56 -0
  25. package/dist/email/elements.d.ts +2 -0
  26. package/dist/email/elements.js +299 -0
  27. package/dist/email/html.d.ts +42 -0
  28. package/dist/email/html.js +266 -0
  29. package/dist/email/jsx-dev-runtime.d.ts +26 -0
  30. package/dist/email/jsx-dev-runtime.js +12 -0
  31. package/dist/email/jsx-runtime.d.ts +2 -0
  32. package/dist/email/jsx-runtime.js +60 -0
  33. package/dist/email/render.d.ts +43 -0
  34. package/dist/email/render.js +6 -0
  35. package/dist/email/selection.d.ts +102 -0
  36. package/dist/email/selection.js +143 -0
  37. package/dist/email/source-edit.d.ts +67 -0
  38. package/dist/email/source-edit.js +185 -0
  39. package/dist/email/stylesheets.d.ts +15 -0
  40. package/dist/email/stylesheets.js +45 -0
  41. package/dist/email/text.d.ts +7 -0
  42. package/dist/email/text.js +100 -0
  43. package/dist/emit.d.ts +13 -12
  44. package/dist/emit.js +63 -63
  45. package/dist/input-schema-BIfzI63B.d.ts +23 -0
  46. package/dist/input-schema.d.ts +2 -0
  47. package/dist/input-schema.js +170 -0
  48. package/dist/jsx-dev-runtime.d.ts +4 -3
  49. package/dist/jsx-dev-runtime.js +9 -1
  50. package/dist/{jsx-runtime-eNIMY87F.d.ts → jsx-runtime-C5k_Zq0A.d.ts} +3 -3
  51. package/dist/jsx-runtime.d.ts +1 -1
  52. package/dist/jsx-runtime.js +20 -1
  53. package/dist/model.d.ts +1 -1
  54. package/dist/ops.d.ts +1 -1
  55. package/dist/ops.js +1 -1
  56. package/dist/{parser-DkQKIjDa.d.ts → parser-CGBS73Gz.d.ts} +2 -2
  57. package/dist/parser.d.ts +1 -1
  58. package/dist/presets/category-mapping.d.ts +1 -1
  59. package/dist/presets/corpus.d.ts +2 -0
  60. package/dist/presets/corpus.js +247 -0
  61. package/dist/presets/public-gallery.d.ts +290 -2
  62. package/dist/presets/public-gallery.js +428 -6
  63. package/dist/presets/public-handoff.d.ts +164 -2
  64. package/dist/presets/public-handoff.js +229 -61
  65. package/dist/presets/public-preview-artifacts.d.ts +2 -2
  66. package/dist/presets/public-preview-artifacts.js +25 -31
  67. package/dist/presets/public-preview.d.ts +65 -2
  68. package/dist/presets/public-preview.js +402 -1152
  69. package/dist/presets/sources.d.ts +35 -4
  70. package/dist/presets/sources.js +2328 -43
  71. package/dist/presets.d.ts +39 -5
  72. package/dist/presets.js +96 -0
  73. package/dist/printer.d.ts +1 -1
  74. package/dist/render-template.d.ts +5 -2
  75. package/dist/render-template.js +3 -1
  76. package/dist/render-ykQhtMs_.js +592 -0
  77. package/dist/source-locations-BCJQnIfU.d.ts +89 -0
  78. package/dist/source-locations.d.ts +2 -0
  79. package/dist/source-locations.js +125 -0
  80. package/dist/template-Dy5yOPyp.d.ts +60 -0
  81. package/dist/template.d.ts +2 -0
  82. package/dist/template.js +37 -0
  83. package/dist/tsx-source-instrumentation.d.ts +23 -0
  84. package/dist/tsx-source-instrumentation.js +97 -0
  85. package/dist/tsx-tracer.d.ts +116 -0
  86. package/dist/tsx-tracer.js +985 -0
  87. package/generated/sml-agent-contract.json +574 -782
  88. package/generated/sml-email-agent-contract.json +502 -677
  89. package/generated/sml-email-language-reference.md +94 -160
  90. package/generated/sml-language-reference.md +195 -220
  91. package/package.json +111 -9
  92. package/dist/public-gallery-D0Zv0pZE.d.ts +0 -611
  93. package/dist/public-preview-u1Z9aaGq.d.ts +0 -1168
package/AGENTS.md CHANGED
@@ -1,8 +1,28 @@
1
1
  # Samva Markup consumer guide
2
2
 
3
- Author TSX with `jsxImportSource: "@samva/markup"`. Import components from
4
- `@samva/markup/components` and variable types from `@samva/markup/variables`; a template file should
5
- default-export at most one template. Use documented package subpaths only.
3
+ Email entries default-export `defineEmail` definitions. SMS and WhatsApp use the structured
4
+ channel runtime at `@samva/markup` and components at `@samva/markup/components`.
5
+ Use documented package subpaths only. Template entry discovery is `.tsx` only.
6
6
 
7
- Samva templates emit SML for server-side compilation. Do not replace SML with precompiled HTML in
8
- CLI push workflows. Use `@samva/vite` for discovery, local preview, and `buildTemplates()`.
7
+ Object email templates use `jsxImportSource: "@samva/markup/email"` and
8
+ `defineEmail` from `@samva/markup/template`. Their render function receives actual validated
9
+ JSON input and returns subject, optional preheader, body, and optional text. A template id and a
10
+ fixture key are both written into file paths, so each must match `TEMPLATE_NAME_PATTERN` — one
11
+ plain path segment — and `defineEmail` refuses anything else where the name is declared. A consumer
12
+ that resolves a path from those names imports that pattern; it never restates it. The derived
13
+ semantic tree lowers directly to HTML/text via `@samva/markup/email/render`; do not route object
14
+ templates through recording proxies or textual SML. Authoring-source provenance belongs to semantic
15
+ nodes.
16
+
17
+ Ordinary HTML is typed. `@samva/markup/email/elements` is the one table behind both the JSX
18
+ intrinsic prop types and the serializer's allowlist, so an unsupported element or attribute fails at
19
+ typecheck and again at compile with a source location. Email primitives — `Email`, `Section`,
20
+ `Columns`/`Column`, `Button`, `Spacer`, `Divider`, `Image`, `Link` — come from
21
+ `@samva/markup/email/components`; they generate table markup and classic-Outlook fallbacks, and
22
+ every wrapper they generate carries the authoring call site as its origin. Findings are
23
+ `EmailDiagnostic` values from `@samva/markup/email/diagnostics`: errors throw `EmailCompileError`,
24
+ warnings ride back on the render result. Nothing an author passes as a string becomes markup.
25
+
26
+ `@samva/markup/compiler` is the bundler-agnostic compiler boundary. `@samva/vite` and
27
+ `apps/agents/src/editor` are adapters over it — they own resolution, watching and transport, never
28
+ compilation semantics — and the module imports no Vite, esbuild or Node builtins.
package/README.md CHANGED
@@ -9,33 +9,99 @@ directly for TSX templates or together with `@samva/vite` for the visual editor
9
9
  npm install --save-dev @samva/markup @samva/vite vite
10
10
  ```
11
11
 
12
- Configure TypeScript to use the Samva JSX runtime:
13
-
14
- ```json
15
- {
16
- "compilerOptions": {
17
- "jsx": "react-jsx",
18
- "jsxImportSource": "@samva/markup"
19
- }
20
- }
21
- ```
22
-
23
- Then author a default-exported template:
12
+ Configure TypeScript with `jsx: "react-jsx"` and
13
+ `jsxImportSource: "@samva/markup/email"`. Install `effect` for this schema example
14
+ (or use a Zod 4 schema). Email entries use `.tsx` and default-export a definition:
24
15
 
25
16
  ```tsx
26
- import { Email, Heading, Text } from "@samva/markup/components";
27
- import type { Variables } from "@samva/markup/variables";
28
-
29
- export default function Welcome(v: Variables<{ firstName: string }>) {
30
- return (
31
- <Email>
32
- <Heading level={1}>Welcome, {v.firstName}</Heading>
33
- <Text>Thanks for joining Samva.</Text>
34
- </Email>
35
- );
36
- }
17
+ /** @jsxImportSource @samva/markup/email */
18
+ import { Schema } from "effect";
19
+ import { Email, Section } from "@samva/markup/email/components";
20
+ import { defineEmail } from "@samva/markup/template";
21
+
22
+ export default defineEmail({
23
+ id: "welcome",
24
+ schema: Schema.toStandardJSONSchemaV1(
25
+ Schema.Struct({ firstName: Schema.String, items: Schema.Array(Schema.String) }),
26
+ ),
27
+ fixtures: { basic: { firstName: "Ada", items: ["Your first project"] } },
28
+ render: (input) => ({
29
+ subject: `Welcome, ${input.firstName}`,
30
+ preheader: "Your account is ready",
31
+ body: (
32
+ <Email>
33
+ <Section>
34
+ <h1>Welcome, {input.firstName}</h1>
35
+ {input.items.length > 0 && (
36
+ <ul>
37
+ {input.items.map((item) => (
38
+ <li key={item}>{item}</li>
39
+ ))}
40
+ </ul>
41
+ )}
42
+ </Section>
43
+ </Email>
44
+ ),
45
+ }),
46
+ });
37
47
  ```
38
48
 
39
- The public subpaths also expose the SML parser, printer, compiler, diagnostics, variables, theme
40
- primitives, channel compilers, and gallery preset data. Prefer the highest-level entrypoint that
41
- matches the task instead of depending on implementation files.
49
+ The definition owns a stable, project-unique `id`, a portable JSON input schema, named fixtures,
50
+ and a synchronous render function. Imported helpers are ordinary modules, not catalog entries.
51
+ Email executes real TypeScript against validated input and lowers a semantic tree to HTML and text.
52
+ SMS and WhatsApp keep their structured channel compiler and `@samva/markup` JSX runtime.
53
+
54
+ ### Explicit template inputs
55
+
56
+ `@samva/markup/template` exports `defineEmail({ id, schema, fixtures, render })`, the email
57
+ channel's authoring helper; `TemplateDefinition` and `TemplateInput` are channel-neutral.
58
+ The schema must implement Standard **JSON Schema** v1; a validator-only Standard Schema is
59
+ insufficient. `Schema.toStandardJSONSchemaV1` (Effect) and Zod 4 schemas support caller-input
60
+ inference, including imported render functions and named fixtures. `TemplateInput<typeof template>`
61
+ extracts the caller type. Rendering receives input unchanged.
62
+
63
+ The portable profile uses JSON Schema 2020-12. Supported validation keywords are `type`, `enum`,
64
+ `const`, `required`, numeric bounds and `multipleOf`, string lengths and `pattern`, object/array
65
+ size bounds, `uniqueItems`, `properties`, `patternProperties`, `additionalProperties`,
66
+ `dependentSchemas`, `propertyNames`, `items`, `prefixItems`, `contains`, `allOf`, `anyOf`, `oneOf`,
67
+ `not`, `if`, `then`, `else`, `$defs`, and local JSON Pointer `$ref`. Boolean schemas are supported.
68
+ Remote references, alternative dialects, `$id`, dynamic references, `format`, and unknown keywords
69
+ fail. Regex constraints use the validator's Unicode JavaScript regular-expression semantics.
70
+
71
+ `title`, `description`, `default`, `examples`, `deprecated`, `readOnly`, `writeOnly`, and `$comment`
72
+ are annotations retained in the complete schema but excluded from `validationIdentity`. Defaults
73
+ are never inserted, values are never coerced, and extra properties are never removed. Identity
74
+ sorts schema object keys; it is a canonical document, not a semantic-equivalence proof.
75
+
76
+ Converters must export portable input and output schemas with equal validation documents.
77
+ Transforms that cannot export output, differing input/output documents, and non-JSON exports fail.
78
+ Custom library refinements are not portable merely because their validator works: converters must
79
+ reject unsupported refinements instead of silently dropping them. This interface cannot detect an
80
+ arbitrary dishonest or lossy converter. The tested Effect and Zod fixtures cover structural nested
81
+ objects, arrays, primitive constraints, and rejection of nonportable exports.
82
+
83
+ The pinned Zod/Effect adapter rejects lazy/suspended schema nodes before executing their getters;
84
+ author recursive contracts directly using local JSON Schema references. Custom predicate metadata
85
+ is checked for these vendors, but a converter-supplied JSON Schema constraint remains its contract.
86
+
87
+ ### Actual-input email JSX
88
+
89
+ Use `jsxImportSource: "@samva/markup/email"` for object template bodies. Ordinary HTML JSX,
90
+ imported synchronous components, nested JSON, conditionals and mapped arrays lower directly to a
91
+ semantic tree. `renderEmail(template, data)` validates unchanged input and returns subject,
92
+ preheader, escaped HTML, explicit or derived text, and the derived tree. Development JSX preserves
93
+ source origins and component call sites. This synchronous library call requires an isolated host
94
+ before executing untrusted source; it is not itself a sandbox.
95
+
96
+ The rendering host has no ambient clock and no entropy source. Reading the current instant or a
97
+ random value throws unless the template declares the matching fixed value in `runtime`, which takes
98
+ an optional `now` (epoch milliseconds) and `randomSeed` (32-bit unsigned integer). A declaration is
99
+ recorded on the publication and applies to every render, including module initializers that capture
100
+ a value at import. `new Date(value)` with an explicit argument always works, so a message either
101
+ takes its time from validated input or states it once as configuration. Locale (`en-US`) and time
102
+ zone (UTC) are publication-wide and back every `Intl` constructor and `toLocale*` call.
103
+
104
+ Portable validation uses `@cfworker/json-schema` 4.1.1 without runtime code generation. The
105
+ 2020-12 meta-schema is checked at every supported schema position, and the explicit profile rejects
106
+ keywords outside its contract before interpretation. The AJV dependency supplies static meta-schema
107
+ JSON; its code generator is not invoked in a loaded template.
@@ -8,6 +8,19 @@ declare const AUXILIARY_TAGS: readonly ["Source"];
8
8
  /** Structured-channel tags: the sms/whatsapp vocabulary beyond shared If/Else. */
9
9
  declare const CHANNEL_TAGS: readonly ["Sms", "WhatsApp", "Header", "Body", "Footer", "Buttons"];
10
10
  declare const SML_TAGS: readonly ["Email", "Subject", "Preview", "Container", "Section", "Row", "Column", "Heading", "Text", "Button", "Image", "Divider", "Spacer", "List", "ListItem", "If", "Else", "strong", "em", "a", "Source", "Sms", "WhatsApp", "Header", "Body", "Footer", "Buttons"];
11
+ /**
12
+ * JSX component name → the SML tag that component emits. TSX authors write
13
+ * capitalized component names, and the inline tags are lowercase in SML, so
14
+ * `<Strong>` emits `strong` and `<A>` emits `a`. The table is derived from
15
+ * `SML_TAGS` rather than written out, so a new tag is mapped the moment it
16
+ * joins the vocabulary. `components.ts` is held to it by the component test
17
+ * suite, and `tsx-tracer.ts` resolves authored JSX names through it.
18
+ */
19
+ declare const SML_TAG_BY_COMPONENT_NAME: Readonly<Record<string, SmlTag>>;
20
+ /** The SML tag a JSX component name emits, or `undefined` when it is not a Samva primitive. */
21
+ declare const smlTagForComponentName: (name: string) => SmlTag | undefined;
22
+ /** The JSX component name that emits an SML tag, for printing SML back into TSX. */
23
+ declare const componentNameForSmlTag: (tag: SmlTag) => string;
11
24
  type BlockTag = (typeof BLOCK_TAGS)[number];
12
25
  type InlineTag = (typeof INLINE_TAGS)[number];
13
26
  type AuxiliaryTag = (typeof AUXILIARY_TAGS)[number];
@@ -116,5 +129,5 @@ declare const isHiddenNode: (node: ElementNode) => boolean;
116
129
  */
117
130
  declare const collectVariableReferences: (document: SmlDocument) => ReadonlySet<string>;
118
131
  //#endregion
119
- export { attributeValue as A, SmlDocument as C, SmlTag as D, SmlNodeSchema as E, nodeLabel as F, numberAttribute as I, stringAttribute as L, documentChannel as M, isHiddenNode as N, TextNode as O, isRootTag as P, SML_TAGS as S, SmlNode as T, InlineTag as _, AttributeValueSchema as a, ROOT_TAGS as b, BlockTag as c, Channel as d, ChannelTag as f, INLINE_TAGS as g, ElementNodeSchema as h, AttributeValue as i, collectVariableReferences as j, TextNodeSchema as k, CHANNEL_BY_ROOT_TAG as l, ElementNode as m, Attribute as n, AuxiliaryTag as o, DEFAULT_EMAIL_WIDTH as p, AttributeSchema as r, BLOCK_TAGS as s, AUXILIARY_TAGS as t, CHANNEL_TAGS as u, InterpolationNode as v, SmlDocumentSchema as w, RootTag as x, InterpolationNodeSchema as y };
120
- //# sourceMappingURL=ast-DAhnAvQN.d.ts.map
132
+ export { TextNodeSchema as A, stringAttribute as B, SML_TAG_BY_COMPONENT_NAME as C, SmlNodeSchema as D, SmlNode as E, isHiddenNode as F, isRootTag as I, nodeLabel as L, collectVariableReferences as M, componentNameForSmlTag as N, SmlTag as O, documentChannel as P, numberAttribute as R, SML_TAGS as S, SmlDocumentSchema as T, InlineTag as _, AttributeValueSchema as a, ROOT_TAGS as b, BlockTag as c, Channel as d, ChannelTag as f, INLINE_TAGS as g, ElementNodeSchema as h, AttributeValue as i, attributeValue as j, TextNode as k, CHANNEL_BY_ROOT_TAG as l, ElementNode as m, Attribute as n, AuxiliaryTag as o, DEFAULT_EMAIL_WIDTH as p, AttributeSchema as r, BLOCK_TAGS as s, AUXILIARY_TAGS as t, CHANNEL_TAGS as u, InterpolationNode as v, SmlDocument as w, RootTag as x, InterpolationNodeSchema as y, smlTagForComponentName as z };
133
+ //# sourceMappingURL=ast-63I-VLQx.d.ts.map
package/dist/ast.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as attributeValue, C as SmlDocument, D as SmlTag, E as SmlNodeSchema, F as nodeLabel, I as numberAttribute, L as stringAttribute, M as documentChannel, N as isHiddenNode, O as TextNode, P as isRootTag, S as SML_TAGS, T as SmlNode, _ as InlineTag, a as AttributeValueSchema, b as ROOT_TAGS, c as BlockTag, d as Channel, f as ChannelTag, g as INLINE_TAGS, h as ElementNodeSchema, i as AttributeValue, j as collectVariableReferences, k as TextNodeSchema, l as CHANNEL_BY_ROOT_TAG, m as ElementNode, n as Attribute, o as AuxiliaryTag, p as DEFAULT_EMAIL_WIDTH, r as AttributeSchema, s as BLOCK_TAGS, t as AUXILIARY_TAGS, u as CHANNEL_TAGS, v as InterpolationNode, w as SmlDocumentSchema, x as RootTag, y as InterpolationNodeSchema } from "./ast-DAhnAvQN.js";
2
- export { AUXILIARY_TAGS, Attribute, AttributeSchema, AttributeValue, AttributeValueSchema, AuxiliaryTag, BLOCK_TAGS, BlockTag, CHANNEL_BY_ROOT_TAG, CHANNEL_TAGS, Channel, ChannelTag, DEFAULT_EMAIL_WIDTH, ElementNode, ElementNodeSchema, INLINE_TAGS, InlineTag, InterpolationNode, InterpolationNodeSchema, ROOT_TAGS, RootTag, SML_TAGS, SmlDocument, SmlDocumentSchema, SmlNode, SmlNodeSchema, SmlTag, TextNode, TextNodeSchema, attributeValue, collectVariableReferences, documentChannel, isHiddenNode, isRootTag, nodeLabel, numberAttribute, stringAttribute };
1
+ import { A as TextNodeSchema, B as stringAttribute, C as SML_TAG_BY_COMPONENT_NAME, D as SmlNodeSchema, E as SmlNode, F as isHiddenNode, I as isRootTag, L as nodeLabel, M as collectVariableReferences, N as componentNameForSmlTag, O as SmlTag, P as documentChannel, R as numberAttribute, S as SML_TAGS, T as SmlDocumentSchema, _ as InlineTag, a as AttributeValueSchema, b as ROOT_TAGS, c as BlockTag, d as Channel, f as ChannelTag, g as INLINE_TAGS, h as ElementNodeSchema, i as AttributeValue, j as attributeValue, k as TextNode, l as CHANNEL_BY_ROOT_TAG, m as ElementNode, n as Attribute, o as AuxiliaryTag, p as DEFAULT_EMAIL_WIDTH, r as AttributeSchema, s as BLOCK_TAGS, t as AUXILIARY_TAGS, u as CHANNEL_TAGS, v as InterpolationNode, w as SmlDocument, x as RootTag, y as InterpolationNodeSchema, z as smlTagForComponentName } from "./ast-63I-VLQx.js";
2
+ export { AUXILIARY_TAGS, Attribute, AttributeSchema, AttributeValue, AttributeValueSchema, AuxiliaryTag, BLOCK_TAGS, BlockTag, CHANNEL_BY_ROOT_TAG, CHANNEL_TAGS, Channel, ChannelTag, DEFAULT_EMAIL_WIDTH, ElementNode, ElementNodeSchema, INLINE_TAGS, InlineTag, InterpolationNode, InterpolationNodeSchema, ROOT_TAGS, RootTag, SML_TAGS, SML_TAG_BY_COMPONENT_NAME, SmlDocument, SmlDocumentSchema, SmlNode, SmlNodeSchema, SmlTag, TextNode, TextNodeSchema, attributeValue, collectVariableReferences, componentNameForSmlTag, documentChannel, isHiddenNode, isRootTag, nodeLabel, numberAttribute, smlTagForComponentName, stringAttribute };
package/dist/ast.js CHANGED
@@ -43,6 +43,19 @@ const SML_TAGS = [
43
43
  ...AUXILIARY_TAGS,
44
44
  ...CHANNEL_TAGS
45
45
  ];
46
+ /**
47
+ * JSX component name → the SML tag that component emits. TSX authors write
48
+ * capitalized component names, and the inline tags are lowercase in SML, so
49
+ * `<Strong>` emits `strong` and `<A>` emits `a`. The table is derived from
50
+ * `SML_TAGS` rather than written out, so a new tag is mapped the moment it
51
+ * joins the vocabulary. `components.ts` is held to it by the component test
52
+ * suite, and `tsx-tracer.ts` resolves authored JSX names through it.
53
+ */
54
+ const SML_TAG_BY_COMPONENT_NAME = Object.freeze(Object.fromEntries(SML_TAGS.map((tag) => [`${tag[0].toUpperCase()}${tag.slice(1)}`, tag])));
55
+ /** The SML tag a JSX component name emits, or `undefined` when it is not a Samva primitive. */
56
+ const smlTagForComponentName = (name) => SML_TAG_BY_COMPONENT_NAME[name];
57
+ /** The JSX component name that emits an SML tag, for printing SML back into TSX. */
58
+ const componentNameForSmlTag = (tag) => `${tag[0].toUpperCase()}${tag.slice(1)}`;
46
59
  /** Resolved primary width for email documents that omit `<Email width={…}>`. */
47
60
  const DEFAULT_EMAIL_WIDTH = 600;
48
61
  /** Root tag → channel. The root tag IS the channel declaration. */
@@ -154,6 +167,6 @@ const collectVariableReferences = (document) => {
154
167
  return names;
155
168
  };
156
169
  //#endregion
157
- export { AUXILIARY_TAGS, AttributeSchema, AttributeValueSchema, BLOCK_TAGS, CHANNEL_BY_ROOT_TAG, CHANNEL_TAGS, DEFAULT_EMAIL_WIDTH, ElementNodeSchema, INLINE_TAGS, InterpolationNodeSchema, ROOT_TAGS, SML_TAGS, SmlDocumentSchema, SmlNodeSchema, TextNodeSchema, attributeValue, collectVariableReferences, documentChannel, isHiddenNode, isRootTag, nodeLabel, numberAttribute, stringAttribute };
170
+ export { AUXILIARY_TAGS, AttributeSchema, AttributeValueSchema, BLOCK_TAGS, CHANNEL_BY_ROOT_TAG, CHANNEL_TAGS, DEFAULT_EMAIL_WIDTH, ElementNodeSchema, INLINE_TAGS, InterpolationNodeSchema, ROOT_TAGS, SML_TAGS, SML_TAG_BY_COMPONENT_NAME, SmlDocumentSchema, SmlNodeSchema, TextNodeSchema, attributeValue, collectVariableReferences, componentNameForSmlTag, documentChannel, isHiddenNode, isRootTag, nodeLabel, numberAttribute, smlTagForComponentName, stringAttribute };
158
171
 
159
172
  //# sourceMappingURL=ast.js.map
@@ -58591,6 +58591,39 @@ const PRIMITIVE_FEATURES = {
58591
58591
  "css-font-size"
58592
58592
  ]
58593
58593
  };
58594
+ const DELIVERY_ASSET_ATTRIBUTES = {
58595
+ Container: ["backgroundImage"],
58596
+ Image: ["src"],
58597
+ Section: ["backgroundImage"],
58598
+ Source: ["src"]
58599
+ };
58600
+ /**
58601
+ * Finds site-relative assets that work in Vite preview but cannot resolve
58602
+ * after an email leaves the author's site. Values backed entirely by one
58603
+ * variable are excluded because their runtime value is a delivery concern.
58604
+ */
58605
+ const checkDeliveryAssetDiagnostics = (source) => {
58606
+ const parsed = parseSml(source);
58607
+ if (Result.isFailure(parsed)) return [];
58608
+ const diagnostics = [];
58609
+ const visit = (node) => {
58610
+ if (node.kind !== "element") return;
58611
+ for (const name of DELIVERY_ASSET_ATTRIBUTES[node.tag] ?? []) {
58612
+ const attribute = node.attributes.find((candidate) => candidate.name === name);
58613
+ if (attribute?.value.kind !== "string") continue;
58614
+ const value = attribute.value.value.trim();
58615
+ if (!value.startsWith("/") || value.startsWith("//") || /^\{\{[^{}]+\}\}$/.test(value)) continue;
58616
+ diagnostics.push({
58617
+ code: "site-relative-delivery-asset",
58618
+ message: `static ${name}=${JSON.stringify(value)} will not resolve in delivered email; use an absolute URL, a data URL, or a variable-backed asset`,
58619
+ span: attribute.valueSpan
58620
+ });
58621
+ }
58622
+ for (const child of node.children) visit(child);
58623
+ };
58624
+ visit(parsed.success.root);
58625
+ return diagnostics;
58626
+ };
58594
58627
  function isBreakingCompatibilityCheck(message) {
58595
58628
  return message.startsWith("error:");
58596
58629
  }
@@ -58912,11 +58945,12 @@ function checkSmlCompatibility(input) {
58912
58945
  const analysis = analyzeDocument(parsed.success);
58913
58946
  return [
58914
58947
  ...predicateDiagnosticMessages(input.source, parsed.success, input.declaredVariables),
58948
+ ...checkDeliveryAssetDiagnostics(input.source).map((diagnostic) => `error: ${formatDiagnostic(input.source, diagnostic)}`),
58915
58949
  ...caniemailMessages(analysis.featureSlugs, profile),
58916
58950
  ...darkModeMessages(analysis, profile)
58917
58951
  ];
58918
58952
  }
58919
58953
  //#endregion
58920
- export { worstCaseArtifactBytes as a, isBreakingCompatibilityCheck as i, checkSmlCompatibility as n, collectSmlFeatureSlugs as r, DEFAULT_EMAIL_TARGET_CLIENT_PROFILE as t };
58954
+ export { isBreakingCompatibilityCheck as a, collectSmlFeatureSlugs as i, checkDeliveryAssetDiagnostics as n, worstCaseArtifactBytes as o, checkSmlCompatibility as r, DEFAULT_EMAIL_TARGET_CLIENT_PROFILE as t };
58921
58955
 
58922
- //# sourceMappingURL=checks-D-zFQt5R.js.map
58956
+ //# sourceMappingURL=checks-DXuERO4B.js.map
package/dist/checks.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { t as Diagnostic } from "./diagnostics-oveE-laV.js";
1
2
  //#region src/checks.d.ts
2
3
  type EmailClientDarkBehavior = "honors" | "force-inverts" | "no dark mode";
3
4
  interface EmailClientTarget {
@@ -17,10 +18,16 @@ interface EmailCompatibilityInput {
17
18
  readonly declaredVariables?: readonly string[] | undefined;
18
19
  }
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[];
20
27
  declare function isBreakingCompatibilityCheck(message: string): boolean;
21
28
  declare function collectSmlFeatureSlugs(source: string): readonly string[];
22
29
  declare function worstCaseArtifactBytes(html: string): number;
23
30
  declare function checkSmlCompatibility(input: EmailCompatibilityInput): readonly string[];
24
31
  //#endregion
25
- export { DEFAULT_EMAIL_TARGET_CLIENT_PROFILE, EmailClientDarkBehavior, EmailClientTarget, EmailCompatibilityInput, TargetClientProfile, checkSmlCompatibility, collectSmlFeatureSlugs, isBreakingCompatibilityCheck, worstCaseArtifactBytes };
32
+ export { DEFAULT_EMAIL_TARGET_CLIENT_PROFILE, EmailClientDarkBehavior, EmailClientTarget, EmailCompatibilityInput, TargetClientProfile, checkDeliveryAssetDiagnostics, checkSmlCompatibility, collectSmlFeatureSlugs, isBreakingCompatibilityCheck, worstCaseArtifactBytes };
26
33
  //# sourceMappingURL=checks.d.ts.map
package/dist/checks.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as worstCaseArtifactBytes, i as isBreakingCompatibilityCheck, n as checkSmlCompatibility, r as collectSmlFeatureSlugs, t as DEFAULT_EMAIL_TARGET_CLIENT_PROFILE } from "./checks-D-zFQt5R.js";
2
- export { DEFAULT_EMAIL_TARGET_CLIENT_PROFILE, checkSmlCompatibility, collectSmlFeatureSlugs, isBreakingCompatibilityCheck, worstCaseArtifactBytes };
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-DXuERO4B.js";
2
+ export { DEFAULT_EMAIL_TARGET_CLIENT_PROFILE, checkDeliveryAssetDiagnostics, checkSmlCompatibility, collectSmlFeatureSlugs, isBreakingCompatibilityCheck, worstCaseArtifactBytes };
@@ -1,6 +1,6 @@
1
1
  import { t as Diagnostic } from "./diagnostics-oveE-laV.js";
2
- import { m as ElementNode } from "./ast-DAhnAvQN.js";
3
- import { a as SMS_TAG_SPECS } from "./parser-DkQKIjDa.js";
2
+ import { m as ElementNode } from "./ast-63I-VLQx.js";
3
+ import { a as SMS_TAG_SPECS } from "./parser-CGBS73Gz.js";
4
4
  import { t as SmsEncoding } from "./sms-segments-y4FpPilJ.js";
5
5
  //#region src/compile-sms.d.ts
6
6
  /** Documented worst-case rendered width of one `{{variable}}`. */
@@ -1,6 +1,6 @@
1
1
  import { a as SmlCompileError, t as Diagnostic } from "./diagnostics-oveE-laV.js";
2
- import { m as ElementNode } from "./ast-DAhnAvQN.js";
3
- import { l as WHATSAPP_TAG_SPECS } from "./parser-DkQKIjDa.js";
2
+ import { m as ElementNode } from "./ast-63I-VLQx.js";
3
+ import { l as WHATSAPP_TAG_SPECS } from "./parser-CGBS73Gz.js";
4
4
  import { Result } from "effect";
5
5
  //#region src/compile-whatsapp.d.ts
6
6
  type WhatsAppSpecs = typeof WHATSAPP_TAG_SPECS;
package/dist/compile.d.ts CHANGED
@@ -1,7 +1,5 @@
1
- import { a as SmlCompileError, o as SmlParseError, t as Diagnostic } from "./diagnostics-oveE-laV.js";
2
- import { C as SmlDocument } from "./ast-DAhnAvQN.js";
3
- import { a as ThemeLayers, s as TokenTable } from "./theme-6ApjjiBo.js";
4
- import { Declaration } from "./registry.js";
1
+ import { a as SmlCompileError, o as SmlParseError } from "./diagnostics-oveE-laV.js";
2
+ import { w as SmlDocument } from "./ast-63I-VLQx.js";
5
3
  import { CompiledSms } from "./compile-sms.js";
6
4
  import { CompiledWhatsApp } from "./compile-whatsapp.js";
7
5
  import { Result } from "effect";
@@ -9,85 +7,12 @@ import { Result } from "effect";
9
7
  declare const COMPILER_VERSION = "0.1.0";
10
8
  //#endregion
11
9
  //#region src/compile.d.ts
12
- type CompileBase = "outlook-safe" | "modern";
13
- /** Email-only knobs; sms/whatsapp compile ignores both (no theming, no bases). */
14
- interface CompileOptions {
15
- readonly base?: CompileBase | undefined;
16
- readonly theme?: (Omit<ThemeLayers, "base"> & {
17
- readonly base?: TokenTable | undefined;
18
- }) | undefined;
19
- /** Omit editor DOM instrumentation from exported/sent HTML artifacts. */
20
- readonly stripInstrumentation?: boolean | undefined;
21
- }
22
- interface CompiledEmail {
23
- readonly channel: "email";
24
- readonly html: string;
25
- /** Plain-text alternative ({{vars}} verbatim, envelope metadata skipped). */
26
- readonly text: string;
27
- /** Authored <Subject> text with interpolation tokens preserved; null when absent. */
28
- readonly subjectText: string | null;
29
- readonly warnings: ReadonlyArray<Diagnostic>;
30
- readonly base: CompileBase;
31
- readonly compilerVersion: string;
32
- }
33
- /**
34
- * The compile result, discriminated on `channel` — which the root tag
35
- * declares. email → HTML + plain-text artifacts; sms → body + segment math;
36
- * whatsapp → whatsapp_template_variants column shapes.
37
- */
38
- type CompiledTemplate = CompiledEmail | CompiledSms | CompiledWhatsApp;
39
- interface HtmlElement {
40
- readonly kind: "element";
41
- readonly tag: string;
42
- /** Ordered; serialized before style. */
43
- readonly attrs: ReadonlyArray<readonly [string, string]>;
44
- /** Serialized as style="" in order — CSS last-wins handles overrides. */
45
- readonly style: ReadonlyArray<Declaration>;
46
- readonly children: ReadonlyArray<HtmlNode>;
47
- readonly void?: boolean | undefined;
48
- }
49
- type HtmlTextNode = {
50
- readonly kind: "text";
51
- readonly value: string;
52
- };
53
- type HtmlRawNode = {
54
- readonly kind: "raw";
55
- readonly html: string;
56
- };
57
- /** Children spliced into the parent's block flow — <If> lowers to one. */
58
- interface HtmlFragment {
59
- readonly kind: "fragment";
60
- readonly children: ReadonlyArray<HtmlNode>;
61
- }
62
- type HtmlNode = HtmlElement | HtmlTextNode | HtmlRawNode | HtmlFragment;
63
- interface CompileHeadStyleAsset {
64
- readonly kind: "style";
65
- readonly css: string;
66
- /** Serialized style asset as emitted in <head>. */
67
- readonly html: string;
68
- /** True for the Outlook-hidden webfont style block. */
69
- readonly outlookHidden: boolean;
70
- }
71
- interface CompileHeadAssets {
72
- readonly styles: ReadonlyArray<CompileHeadStyleAsset>;
73
- readonly links: ReadonlyArray<HtmlElement>;
74
- }
75
- type HtmlNodeIdMap = Readonly<Record<string, ReadonlyArray<HtmlElement>>>;
76
- interface CompiledEmailArtifact extends CompiledEmail {
77
- /** Resolved primary document width in pixels. */
78
- readonly width: number;
79
- readonly tree: HtmlElement;
80
- readonly doctype: string;
81
- readonly headAssets: CompileHeadAssets;
82
- readonly nodeIdMap: HtmlNodeIdMap;
83
- }
84
- type CompiledTemplateArtifact = CompiledEmailArtifact | CompiledSms | CompiledWhatsApp;
85
- declare const serializeHtmlNode: (node: HtmlNode) => string;
86
- /** Compile a parsed document; the root tag routes to its channel backend. */
87
- declare const compileSml: (document: SmlDocument, options?: CompileOptions) => Result.Result<CompiledTemplate, SmlCompileError>;
88
- declare const compileSmlArtifact: (document: SmlDocument, options?: CompileOptions) => Result.Result<CompiledTemplateArtifact, SmlCompileError>;
89
- declare const compileSource: (source: string, options?: CompileOptions) => Result.Result<CompiledTemplate, SmlParseError | SmlCompileError>;
90
- declare const compileSourceArtifact: (source: string, options?: CompileOptions) => Result.Result<CompiledTemplateArtifact, SmlParseError | SmlCompileError>;
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.Result<CompiledTemplate, SmlCompileError>;
14
+ /** Parse and compile a structured SMS or WhatsApp document. */
15
+ declare const compileSource: (source: string) => Result.Result<CompiledTemplate, SmlParseError | SmlCompileError>;
91
16
  //#endregion
92
- export { COMPILER_VERSION, CompileBase, CompileHeadAssets, CompileHeadStyleAsset, CompileOptions, CompiledEmail, CompiledEmailArtifact, CompiledTemplate, CompiledTemplateArtifact, HtmlElement, HtmlFragment, HtmlNode, HtmlNodeIdMap, HtmlRawNode, HtmlTextNode, compileSml, compileSmlArtifact, compileSource, compileSourceArtifact, serializeHtmlNode };
17
+ export { COMPILER_VERSION, CompiledTemplate, compileSml, compileSource };
93
18
  //# sourceMappingURL=compile.d.ts.map