@samva/markup 0.1.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 (124) hide show
  1. package/AGENTS.md +8 -0
  2. package/LICENSE +21 -0
  3. package/README.md +41 -0
  4. package/dist/ast-DAhnAvQN.d.ts +120 -0
  5. package/dist/ast.d.ts +2 -0
  6. package/dist/ast.js +159 -0
  7. package/dist/ast.js.map +1 -0
  8. package/dist/candidate.d.ts +29 -0
  9. package/dist/candidate.js +88 -0
  10. package/dist/candidate.js.map +1 -0
  11. package/dist/checks-CW0nFj5n.js +58922 -0
  12. package/dist/checks-CW0nFj5n.js.map +1 -0
  13. package/dist/checks.d.ts +26 -0
  14. package/dist/checks.js +2 -0
  15. package/dist/classes.d.ts +27 -0
  16. package/dist/classes.js +184 -0
  17. package/dist/classes.js.map +1 -0
  18. package/dist/compile-sms.d.ts +28 -0
  19. package/dist/compile-sms.js +44 -0
  20. package/dist/compile-sms.js.map +1 -0
  21. package/dist/compile-whatsapp.d.ts +56 -0
  22. package/dist/compile-whatsapp.js +186 -0
  23. package/dist/compile-whatsapp.js.map +1 -0
  24. package/dist/compile.d.ts +93 -0
  25. package/dist/compile.js +938 -0
  26. package/dist/compile.js.map +1 -0
  27. package/dist/components.d.ts +289 -0
  28. package/dist/components.js +106 -0
  29. package/dist/components.js.map +1 -0
  30. package/dist/conditionals.d.ts +10 -0
  31. package/dist/conditionals.js +42 -0
  32. package/dist/conditionals.js.map +1 -0
  33. package/dist/diagnostics-oveE-laV.d.ts +79 -0
  34. package/dist/diagnostics.d.ts +2 -0
  35. package/dist/diagnostics.js +62 -0
  36. package/dist/diagnostics.js.map +1 -0
  37. package/dist/emit.d.ts +50 -0
  38. package/dist/emit.js +151 -0
  39. package/dist/emit.js.map +1 -0
  40. package/dist/footer.d.ts +66 -0
  41. package/dist/footer.js +90 -0
  42. package/dist/footer.js.map +1 -0
  43. package/dist/interpolate.d.ts +27 -0
  44. package/dist/interpolate.js +61 -0
  45. package/dist/interpolate.js.map +1 -0
  46. package/dist/jsx-dev-runtime.d.ts +10 -0
  47. package/dist/jsx-dev-runtime.js +7 -0
  48. package/dist/jsx-dev-runtime.js.map +1 -0
  49. package/dist/jsx-runtime-fSxcvlx4.d.ts +57 -0
  50. package/dist/jsx-runtime.d.ts +2 -0
  51. package/dist/jsx-runtime.js +102 -0
  52. package/dist/jsx-runtime.js.map +1 -0
  53. package/dist/lexer.d.ts +60 -0
  54. package/dist/lexer.js +269 -0
  55. package/dist/lexer.js.map +1 -0
  56. package/dist/model.d.ts +39 -0
  57. package/dist/model.js +90 -0
  58. package/dist/model.js.map +1 -0
  59. package/dist/ops.d.ts +208 -0
  60. package/dist/ops.js +546 -0
  61. package/dist/ops.js.map +1 -0
  62. package/dist/parser-yzjUOGhc.d.ts +387 -0
  63. package/dist/parser.d.ts +2 -0
  64. package/dist/parser.js +821 -0
  65. package/dist/parser.js.map +1 -0
  66. package/dist/predicate-DTtVtLK7.d.ts +49 -0
  67. package/dist/predicate.d.ts +2 -0
  68. package/dist/predicate.js +417 -0
  69. package/dist/predicate.js.map +1 -0
  70. package/dist/presets/category-mapping.d.ts +13 -0
  71. package/dist/presets/category-mapping.js +49 -0
  72. package/dist/presets/category-mapping.js.map +1 -0
  73. package/dist/presets/public-gallery.d.ts +490 -0
  74. package/dist/presets/public-gallery.js +563 -0
  75. package/dist/presets/public-gallery.js.map +1 -0
  76. package/dist/presets/public-handoff.d.ts +68 -0
  77. package/dist/presets/public-handoff.js +105 -0
  78. package/dist/presets/public-handoff.js.map +1 -0
  79. package/dist/presets/public-preview-artifacts.d.ts +41 -0
  80. package/dist/presets/public-preview-artifacts.js +109 -0
  81. package/dist/presets/public-preview-artifacts.js.map +1 -0
  82. package/dist/presets/public-preview.d.ts +1168 -0
  83. package/dist/presets/public-preview.js +1158 -0
  84. package/dist/presets/public-preview.js.map +1 -0
  85. package/dist/presets/sources.d.ts +7 -0
  86. package/dist/presets/sources.js +54 -0
  87. package/dist/presets/sources.js.map +1 -0
  88. package/dist/presets.d.ts +350 -0
  89. package/dist/presets.js +1251 -0
  90. package/dist/presets.js.map +1 -0
  91. package/dist/printer.d.ts +6 -0
  92. package/dist/printer.js +122 -0
  93. package/dist/printer.js.map +1 -0
  94. package/dist/registry.d.ts +42 -0
  95. package/dist/registry.js +646 -0
  96. package/dist/registry.js.map +1 -0
  97. package/dist/render-template.d.ts +42 -0
  98. package/dist/render-template.js +74 -0
  99. package/dist/render-template.js.map +1 -0
  100. package/dist/sms-segments-y4FpPilJ.d.ts +13 -0
  101. package/dist/sms-segments.d.ts +2 -0
  102. package/dist/sms-segments.js +46 -0
  103. package/dist/sms-segments.js.map +1 -0
  104. package/dist/theme-6ApjjiBo.d.ts +116 -0
  105. package/dist/theme-css.d.ts +24 -0
  106. package/dist/theme-css.js +509 -0
  107. package/dist/theme-css.js.map +1 -0
  108. package/dist/theme.d.ts +2 -0
  109. package/dist/theme.js +95 -0
  110. package/dist/theme.js.map +1 -0
  111. package/dist/tokens.d.ts +6 -0
  112. package/dist/tokens.js +417 -0
  113. package/dist/tokens.js.map +1 -0
  114. package/dist/variables-3NGLpNTB.d.ts +77 -0
  115. package/dist/variables.d.ts +2 -0
  116. package/dist/variables.js +141 -0
  117. package/dist/variables.js.map +1 -0
  118. package/dist/version-BITr58kk.js +96 -0
  119. package/dist/version-BITr58kk.js.map +1 -0
  120. package/generated/sml-agent-contract.json +917 -0
  121. package/generated/sml-email-agent-contract.json +790 -0
  122. package/generated/sml-email-language-reference.md +160 -0
  123. package/generated/sml-language-reference.md +220 -0
  124. package/package.json +203 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","names":[],"sources":["../src/registry.ts"],"sourcesContent":["import type { TokenTable } from \"./theme\";\n\n// Declarative utility registry (kumo component-registry pattern): every\n// supported class family is a data row — match root, resolve value against the\n// layered token table, emit literal declarations. Adding a family means adding\n// a row; the resolver (classes.ts) never changes. `description` feeds agent\n// docs and the generated language reference (M8); `caniemailSlugs` wires each\n// family into the checks engine (M3).\n\nexport interface Declaration {\n readonly property: string;\n readonly value: string;\n}\n\nexport interface UtilityFamily {\n readonly root: string;\n /** Accepts `root` with no value (\"border\", \"rounded\", \"block\"). */\n readonly bare?: ReadonlyArray<Declaration> | undefined;\n /** Resolve a named value; null = this family does not accept it. */\n readonly named?:\n | ((value: string, theme: TokenTable) => ReadonlyArray<Declaration> | null)\n | undefined;\n /** Resolve an arbitrary `[...]` value; null = malformed for this family. */\n readonly arbitrary?:\n | ((value: string, theme: TokenTable) => ReadonlyArray<Declaration> | null)\n | undefined;\n /** dark: variant is only legal on color families. */\n readonly colorFamily?: boolean | undefined;\n /**\n * Base-variant only: sm:/dark: are rejected. Set on the gradient family —\n * its declarations are compiler-internal placeholders that must be combined\n * per node, which head rules cannot express.\n */\n readonly baseOnly?: boolean | undefined;\n /** \"warn\" families compile but surface a lint warning. */\n readonly warn?: string | undefined;\n readonly description: string;\n readonly caniemailSlugs: ReadonlyArray<string>;\n}\n\nexport interface ForbiddenUtility {\n /** Exact class or `prefix-` match against the candidate base. */\n readonly match: string;\n readonly message: string;\n}\n\nconst SPACING_PATTERN = /^\\d+(\\.(25|5|75))?$/;\nconst ARBITRARY_LENGTH_PATTERN = /^\\d+(\\.\\d+)?(px|%)$/;\nconst ARBITRARY_PX_PATTERN = /^\\d+(\\.\\d+)?px$/;\nconst ARBITRARY_HEX_PATTERN = /^#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$/;\n\nconst px = (value: number): string => `${roundPx(value)}px`;\n\n// Avoid float dust like 4.800000000000001px for fractional spacing steps.\nconst roundPx = (value: number): number => Math.round(value * 100) / 100;\n\nconst spacingValue = (value: string, theme: TokenTable): string | null => {\n if (value === \"px\") return \"1px\";\n if (!SPACING_PATTERN.test(value)) return null;\n return px(Number.parseFloat(value) * theme.spacingPx);\n};\n\nconst arbitraryLength = (value: string): ReadonlyArray<string> | null =>\n ARBITRARY_LENGTH_PATTERN.test(value) ? [value] : null;\n\nconst colorValue = (value: string, theme: TokenTable): string | null =>\n value === \"transparent\" ? \"transparent\" : (theme.colors[value] ?? null);\n\nconst declarations = (\n properties: ReadonlyArray<string>,\n value: string,\n): ReadonlyArray<Declaration> => properties.map((property) => ({ property, value }));\n\nconst textSizeDeclarations = (sizePx: number): ReadonlyArray<Declaration> => [\n { property: \"font-size\", value: px(sizePx) },\n { property: \"line-height\", value: px(sizePx * 1.5) },\n];\n\nconst arbitraryTextSize = (value: string): ReadonlyArray<Declaration> | null => {\n if (!ARBITRARY_PX_PATTERN.test(value)) return null;\n const size = Number.parseFloat(value);\n return size >= 8 && size <= 72 ? textSizeDeclarations(size) : null;\n};\n\nconst radiusValue = (value: string, theme: TokenTable): string | null => {\n if (value === \"none\") return \"0px\";\n if (value === \"full\") return \"9999px\";\n const radius = theme.radiusPx[value];\n return radius === undefined ? null : px(radius);\n};\n\nconst radiusFamily = (\n root: string,\n properties: ReadonlyArray<string>,\n description: string,\n): UtilityFamily => {\n const family: UtilityFamily = {\n root,\n named: (value, theme) => {\n const resolved = radiusValue(value, theme);\n return resolved === null ? null : declarations(properties, resolved);\n },\n arbitrary: (value) =>\n ARBITRARY_PX_PATTERN.test(value) ? declarations(properties, value) : null,\n description,\n caniemailSlugs: [\"css-border-radius\"],\n };\n return root === \"rounded\"\n ? {\n ...family,\n bare: properties.map((property) => ({ property, value: \"4px\" })),\n }\n : family;\n};\n\nconst spacingFamily = (\n root: string,\n properties: ReadonlyArray<string>,\n options: {\n readonly description: string;\n readonly caniemailSlugs: ReadonlyArray<string>;\n readonly warn?: string | undefined;\n readonly auto?: boolean | undefined;\n },\n): UtilityFamily => ({\n root,\n named: (value, theme) => {\n if (options.auto === true && value === \"auto\") return declarations(properties, \"auto\");\n const resolved = spacingValue(value, theme);\n return resolved === null ? null : declarations(properties, resolved);\n },\n arbitrary: (value) => (ARBITRARY_PX_PATTERN.test(value) ? declarations(properties, value) : null),\n description: options.description,\n caniemailSlugs: options.caniemailSlugs,\n warn: options.warn,\n});\n\nconst MARGIN_WARNING =\n \"margin is stripped by some clients (Outlook.com dark mode, some webmail); prefer padding or <Spacer>\";\n\n// ── Gradient placeholders ─────────────────────────────────────────────────────\n// The gradient family resolves to compiler-internal placeholder declarations;\n// the compiler combines the three per node into real background declarations\n// (modern base: background-image linear-gradient + a solid background-color\n// fallback; outlook-safe base: the solid `from` color only — Outlook for\n// Windows has no gradient support worth a VML hack). They never reach an\n// artifact.\nexport const GRADIENT_DIRECTION_PROPERTY = \"-sml-gradient-direction\";\nexport const GRADIENT_FROM_PROPERTY = \"-sml-gradient-from\";\nexport const GRADIENT_TO_PROPERTY = \"-sml-gradient-to\";\n\nconst GRADIENT_DIRECTIONS: Readonly<Record<string, string>> = {\n \"to-t\": \"to top\",\n \"to-b\": \"to bottom\",\n \"to-l\": \"to left\",\n \"to-r\": \"to right\",\n};\n\nexport const utilityRegistry: ReadonlyArray<UtilityFamily> = [\n // ── Padding ────────────────────────────────────────────────────────────────\n spacingFamily(\"p\", [\"padding\"], {\n description: \"Padding on all sides (p-4 = 16px). Compiles onto table cells for Outlook.\",\n caniemailSlugs: [\"css-padding\"],\n }),\n spacingFamily(\"px\", [\"padding-left\", \"padding-right\"], {\n description: \"Horizontal padding.\",\n caniemailSlugs: [\"css-padding\"],\n }),\n spacingFamily(\"py\", [\"padding-top\", \"padding-bottom\"], {\n description: \"Vertical padding.\",\n caniemailSlugs: [\"css-padding\"],\n }),\n spacingFamily(\"pt\", [\"padding-top\"], {\n description: \"Top padding.\",\n caniemailSlugs: [\"css-padding\"],\n }),\n spacingFamily(\"pr\", [\"padding-right\"], {\n description: \"Right padding.\",\n caniemailSlugs: [\"css-padding\"],\n }),\n spacingFamily(\"pb\", [\"padding-bottom\"], {\n description: \"Bottom padding.\",\n caniemailSlugs: [\"css-padding\"],\n }),\n spacingFamily(\"pl\", [\"padding-left\"], {\n description: \"Left padding.\",\n caniemailSlugs: [\"css-padding\"],\n }),\n\n // ── Margin (warn tier) ─────────────────────────────────────────────────────\n spacingFamily(\"m\", [\"margin\"], {\n description: \"Margin on all sides. Warn tier — some clients strip margins.\",\n caniemailSlugs: [\"css-margin\"],\n warn: MARGIN_WARNING,\n }),\n spacingFamily(\"mx\", [\"margin-left\", \"margin-right\"], {\n description: \"Horizontal margin; mx-auto centers block elements.\",\n caniemailSlugs: [\"css-margin\"],\n warn: MARGIN_WARNING,\n auto: true,\n }),\n spacingFamily(\"my\", [\"margin-top\", \"margin-bottom\"], {\n description: \"Vertical margin.\",\n caniemailSlugs: [\"css-margin\"],\n warn: MARGIN_WARNING,\n }),\n spacingFamily(\"mt\", [\"margin-top\"], {\n description: \"Top margin.\",\n caniemailSlugs: [\"css-margin\"],\n warn: MARGIN_WARNING,\n }),\n spacingFamily(\"mr\", [\"margin-right\"], {\n description: \"Right margin.\",\n caniemailSlugs: [\"css-margin\"],\n warn: MARGIN_WARNING,\n auto: true,\n }),\n spacingFamily(\"mb\", [\"margin-bottom\"], {\n description: \"Bottom margin.\",\n caniemailSlugs: [\"css-margin\"],\n warn: MARGIN_WARNING,\n }),\n spacingFamily(\"ml\", [\"margin-left\"], {\n description: \"Left margin.\",\n caniemailSlugs: [\"css-margin\"],\n warn: MARGIN_WARNING,\n auto: true,\n }),\n\n // ── Sizing ─────────────────────────────────────────────────────────────────\n {\n root: \"w\",\n named: (value, theme) => {\n if (value === \"full\") return [{ property: \"width\", value: \"100%\" }];\n const resolved = spacingValue(value, theme);\n return resolved === null ? null : [{ property: \"width\", value: resolved }];\n },\n arbitrary: (value) => (arbitraryLength(value) === null ? null : [{ property: \"width\", value }]),\n description: \"Width from the spacing scale (w-32 = 128px), w-full, or w-[600px]/w-[50%].\",\n caniemailSlugs: [\"css-width\"],\n },\n {\n root: \"max-w\",\n named: (value, theme) => {\n if (value === \"full\") return [{ property: \"max-width\", value: \"100%\" }];\n if (value === \"none\") return [{ property: \"max-width\", value: \"none\" }];\n const container = theme.containerPx[value];\n return container === undefined ? null : [{ property: \"max-width\", value: px(container) }];\n },\n arbitrary: (value) =>\n arbitraryLength(value) === null ? null : [{ property: \"max-width\", value }],\n description: \"Max width from the container scale (max-w-lg = 512px) or max-w-[600px].\",\n caniemailSlugs: [\"css-max-width\"],\n },\n {\n root: \"h\",\n named: (value, theme) => {\n if (value === \"full\") return [{ property: \"height\", value: \"100%\" }];\n const resolved = spacingValue(value, theme);\n return resolved === null ? null : [{ property: \"height\", value: resolved }];\n },\n arbitrary: (value) =>\n arbitraryLength(value) === null ? null : [{ property: \"height\", value }],\n description: \"Height from the spacing scale (h-12 = 48px) or h-[80px].\",\n caniemailSlugs: [\"css-height\"],\n },\n\n // ── Typography ─────────────────────────────────────────────────────────────\n {\n root: \"text\",\n named: (value, theme) => {\n const size = theme.text[value];\n if (size === undefined) return null;\n return [\n ...declarations([\"font-size\"], px(size.sizePx)),\n ...declarations([\"line-height\"], px(size.lineHeightPx)),\n ];\n },\n arbitrary: arbitraryTextSize,\n description:\n \"Font size with paired line-height (text-sm = 14px/20px) or bounded px value (text-[15px] = 15px/22.5px). Put an explicit leading-* after it to override line-height.\",\n caniemailSlugs: [\"css-font-size\", \"css-line-height\"],\n },\n {\n root: \"text\",\n named: (value) =>\n value === \"left\" || value === \"center\" || value === \"right\" || value === \"justify\"\n ? [{ property: \"text-align\", value }]\n : null,\n description: \"Text alignment: text-left, text-center, text-right, text-justify.\",\n caniemailSlugs: [\"css-text-align\"],\n },\n {\n root: \"text\",\n colorFamily: true,\n named: (value, theme) => {\n const color = colorValue(value, theme);\n return color === null ? null : [{ property: \"color\", value: color }];\n },\n arbitrary: (value) =>\n ARBITRARY_HEX_PATTERN.test(value)\n ? [{ property: \"color\", value: value.toLowerCase() }]\n : null,\n description: \"Text color from the palette (text-gray-700) or text-[#374151].\",\n caniemailSlugs: [\"css-color\"],\n },\n {\n root: \"leading\",\n named: (value, theme) => {\n if (value === \"none\") return [{ property: \"line-height\", value: \"1\" }];\n const ratio = theme.leading[value];\n if (ratio !== undefined) return [{ property: \"line-height\", value: String(ratio) }];\n const resolved = spacingValue(value, theme);\n return resolved === null ? null : [{ property: \"line-height\", value: resolved }];\n },\n arbitrary: (value) =>\n ARBITRARY_PX_PATTERN.test(value) ? [{ property: \"line-height\", value }] : null,\n description: \"Line height: named ratios (leading-relaxed) or spacing steps (leading-6 = 24px).\",\n caniemailSlugs: [\"css-line-height\"],\n },\n {\n root: \"font\",\n named: (value, theme) => {\n const weight = theme.fontWeights[value];\n return weight === undefined ? null : [{ property: \"font-weight\", value: String(weight) }];\n },\n description: \"Font weight: font-normal, font-medium, font-semibold, font-bold, ...\",\n caniemailSlugs: [\"css-font-weight\"],\n },\n {\n root: \"font\",\n named: (value, theme) => {\n const family = theme.fontFamilies[value];\n return family === undefined ? null : [{ property: \"font-family\", value: family }];\n },\n description: \"Font stack: font-sans, font-serif, font-mono.\",\n caniemailSlugs: [\"css-font\"],\n },\n {\n root: \"tracking\",\n named: (value, theme) => {\n const em = theme.trackingEm[value];\n return em === undefined ? null : [{ property: \"letter-spacing\", value: `${em}em` }];\n },\n description:\n \"Letter spacing: tracking-tighter … tracking-widest (em-based; inlines fine in Outlook). Pair tracking-wide/wider with uppercase for eyebrows.\",\n caniemailSlugs: [\"css-letter-spacing\"],\n },\n {\n root: \"uppercase\",\n bare: [{ property: \"text-transform\", value: \"uppercase\" }],\n description: \"Uppercase text transform.\",\n caniemailSlugs: [\"css-text-transform\"],\n },\n {\n root: \"underline\",\n bare: [{ property: \"text-decoration\", value: \"underline\" }],\n description: \"Underlined text.\",\n caniemailSlugs: [\"css-text-decoration\"],\n },\n {\n root: \"no-underline\",\n bare: [{ property: \"text-decoration\", value: \"none\" }],\n description: \"Removes the underline (links render underlined by default in most clients).\",\n caniemailSlugs: [\"css-text-decoration\"],\n },\n\n // ── Color ──────────────────────────────────────────────────────────────────\n {\n root: \"bg\",\n colorFamily: true,\n named: (value, theme) => {\n const color = colorValue(value, theme);\n return color === null ? null : [{ property: \"background-color\", value: color }];\n },\n arbitrary: (value) =>\n ARBITRARY_HEX_PATTERN.test(value)\n ? [{ property: \"background-color\", value: value.toLowerCase() }]\n : null,\n description: \"Background color from the palette (bg-white, bg-blue-600) or bg-[#0088cc].\",\n caniemailSlugs: [\"css-background-color\"],\n },\n\n // ── Gradient (bg-gradient-to-* + from-* + to-*) ────────────────────────────\n {\n root: \"bg-gradient\",\n baseOnly: true,\n named: (value) => {\n const direction = GRADIENT_DIRECTIONS[value];\n return direction === undefined\n ? null\n : [{ property: GRADIENT_DIRECTION_PROPERTY, value: direction }];\n },\n description:\n \"Linear gradient background for hero sections: bg-gradient-to-b/t/l/r with from-{color} and to-{color}. Modern base compiles a CSS linear-gradient (plus a solid from-color fallback); the outlook-safe base degrades to the solid from-color — Outlook for Windows never renders the gradient.\",\n caniemailSlugs: [\"css-linear-gradient\", \"css-background-image\"],\n },\n {\n root: \"from\",\n baseOnly: true,\n named: (value, theme) => {\n const color = colorValue(value, theme);\n return color === null ? null : [{ property: GRADIENT_FROM_PROPERTY, value: color }];\n },\n arbitrary: (value) =>\n ARBITRARY_HEX_PATTERN.test(value)\n ? [{ property: GRADIENT_FROM_PROPERTY, value: value.toLowerCase() }]\n : null,\n description:\n \"Gradient start color (from-indigo-600). Doubles as the solid fallback color on clients without gradient support (Outlook for Windows).\",\n caniemailSlugs: [\"css-linear-gradient\"],\n },\n {\n root: \"to\",\n baseOnly: true,\n named: (value, theme) => {\n const color = colorValue(value, theme);\n return color === null ? null : [{ property: GRADIENT_TO_PROPERTY, value: color }];\n },\n arbitrary: (value) =>\n ARBITRARY_HEX_PATTERN.test(value)\n ? [{ property: GRADIENT_TO_PROPERTY, value: value.toLowerCase() }]\n : null,\n description: \"Gradient end color (to-purple-600).\",\n caniemailSlugs: [\"css-linear-gradient\"],\n },\n\n // ── Border ─────────────────────────────────────────────────────────────────\n {\n root: \"border\",\n bare: [\n { property: \"border-width\", value: \"1px\" },\n { property: \"border-style\", value: \"solid\" },\n ],\n named: (value) =>\n /^\\d+$/.test(value)\n ? [\n { property: \"border-width\", value: `${value}px` },\n { property: \"border-style\", value: \"solid\" },\n ]\n : null,\n description: \"Solid border width: border (1px), border-2, border-0.\",\n caniemailSlugs: [\"css-border\"],\n },\n {\n root: \"border\",\n colorFamily: true,\n named: (value, theme) => {\n const color = colorValue(value, theme);\n return color === null ? null : [{ property: \"border-color\", value: color }];\n },\n arbitrary: (value) =>\n ARBITRARY_HEX_PATTERN.test(value)\n ? [{ property: \"border-color\", value: value.toLowerCase() }]\n : null,\n description: \"Border color from the palette (border-gray-200).\",\n caniemailSlugs: [\"css-border\"],\n },\n radiusFamily(\n \"rounded\",\n [\"border-radius\"],\n \"Border radius (rounded-md = 6px). Ignored by Outlook for Windows — buttons degrade to square corners.\",\n ),\n radiusFamily(\n \"rounded-t\",\n [\"border-top-left-radius\", \"border-top-right-radius\"],\n \"Top border radius for card/header blocks (rounded-t-lg). Degrades to square corners in Outlook for Windows.\",\n ),\n radiusFamily(\n \"rounded-r\",\n [\"border-top-right-radius\", \"border-bottom-right-radius\"],\n \"Right-side border radius (rounded-r-lg). Degrades to square corners in Outlook for Windows.\",\n ),\n radiusFamily(\n \"rounded-b\",\n [\"border-bottom-right-radius\", \"border-bottom-left-radius\"],\n \"Bottom border radius for card/footer blocks (rounded-b-lg). Degrades to square corners in Outlook for Windows.\",\n ),\n radiusFamily(\n \"rounded-l\",\n [\"border-top-left-radius\", \"border-bottom-left-radius\"],\n \"Left-side border radius (rounded-l-lg). Degrades to square corners in Outlook for Windows.\",\n ),\n\n // ── Display ────────────────────────────────────────────────────────────────\n {\n root: \"block\",\n bare: [{ property: \"display\", value: \"block\" }],\n description: \"display: block.\",\n caniemailSlugs: [\"css-display\"],\n },\n {\n root: \"inline-block\",\n bare: [{ property: \"display\", value: \"inline-block\" }],\n description: \"display: inline-block.\",\n caniemailSlugs: [\"css-display\"],\n },\n {\n root: \"hidden\",\n bare: [{ property: \"display\", value: \"none\" }],\n description: \"display: none. Pair with sm:block for mobile-only content.\",\n caniemailSlugs: [\"css-display\"],\n },\n];\n\n// Forbidden utilities are typed compile errors that name the failing client and\n// the supported alternative — drop features rather than break clients.\nexport const forbiddenUtilities: ReadonlyArray<ForbiddenUtility> = [\n {\n match: \"flex\",\n message:\n \"display:flex is unsupported in Outlook for Windows (Word engine) — use <Row>/<Column>\",\n },\n {\n match: \"inline-flex\",\n message: \"display:inline-flex is unsupported in Outlook for Windows — use <Row>/<Column>\",\n },\n {\n match: \"grid\",\n message: \"display:grid is unsupported in Outlook for Windows and Gmail — use <Row>/<Column>\",\n },\n {\n match: \"inline-grid\",\n message: \"display:inline-grid is unsupported in Outlook for Windows — use <Row>/<Column>\",\n },\n {\n match: \"gap-\",\n message: \"gap requires flex/grid, unsupported in Outlook for Windows — use padding on <Column>\",\n },\n {\n match: \"space-\",\n message: \"space-* margins require selectors Gmail strips — use padding or <Spacer>\",\n },\n {\n match: \"items-\",\n message: \"flexbox alignment is unsupported in Outlook for Windows — use <Row>/<Column>\",\n },\n {\n match: \"justify-\",\n message:\n \"flexbox alignment is unsupported in Outlook for Windows — use <Row>/<Column> and text-center\",\n },\n {\n match: \"self-\",\n message: \"flexbox alignment is unsupported in Outlook for Windows — use <Row>/<Column>\",\n },\n {\n match: \"content-\",\n message: \"flexbox alignment is unsupported in Outlook for Windows — use <Row>/<Column>\",\n },\n {\n match: \"order-\",\n message: \"flex order is unsupported in Outlook for Windows — reorder the source instead\",\n },\n {\n match: \"absolute\",\n message: \"CSS position is ignored by Gmail and Outlook — use nested <Section>s\",\n },\n {\n match: \"relative\",\n message: \"CSS position is ignored by Gmail and Outlook — use nested <Section>s\",\n },\n {\n match: \"fixed\",\n message: \"CSS position is ignored by Gmail and Outlook — use nested <Section>s\",\n },\n {\n match: \"sticky\",\n message: \"CSS position is ignored by Gmail and Outlook — use nested <Section>s\",\n },\n { match: \"static\", message: \"CSS position is ignored by Gmail and Outlook — remove it\" },\n { match: \"inset-\", message: \"position offsets are ignored by Gmail and Outlook — use padding\" },\n { match: \"top-\", message: \"position offsets are ignored by Gmail and Outlook — use padding\" },\n { match: \"right-\", message: \"position offsets are ignored by Gmail and Outlook — use padding\" },\n { match: \"bottom-\", message: \"position offsets are ignored by Gmail and Outlook — use padding\" },\n { match: \"left-\", message: \"position offsets are ignored by Gmail and Outlook — use padding\" },\n { match: \"z-\", message: \"z-index is ignored by Gmail and Outlook — remove it\" },\n { match: \"float-\", message: \"float is unreliable across email clients — use <Row>/<Column>\" },\n {\n match: \"shadow\",\n message: \"box-shadow is unsupported in Outlook for Windows and Gmail — use a border instead\",\n },\n {\n match: \"shadow-\",\n message: \"box-shadow is unsupported in Outlook for Windows and Gmail — use a border instead\",\n },\n {\n match: \"opacity-\",\n message: \"opacity is unsupported in Outlook for Windows — bake the tint into the color\",\n },\n { match: \"transform\", message: \"CSS transforms are unsupported in email clients — remove them\" },\n { match: \"rotate-\", message: \"CSS transforms are unsupported in email clients — remove them\" },\n { match: \"scale-\", message: \"CSS transforms are unsupported in email clients — remove them\" },\n { match: \"translate-\", message: \"CSS transforms are unsupported in email clients — remove them\" },\n { match: \"skew-\", message: \"CSS transforms are unsupported in email clients — remove them\" },\n { match: \"transition\", message: \"transitions don't run in email — remove them\" },\n { match: \"transition-\", message: \"transitions don't run in email — remove them\" },\n { match: \"duration-\", message: \"transitions don't run in email — remove them\" },\n { match: \"animate-\", message: \"animations don't run in email — remove them\" },\n { match: \"blur-\", message: \"CSS filters are unsupported in most email clients — remove them\" },\n {\n match: \"brightness-\",\n message: \"CSS filters are unsupported in most email clients — remove them\",\n },\n {\n match: \"grayscale\",\n message: \"CSS filters are unsupported in most email clients — remove them\",\n },\n {\n match: \"grayscale-\",\n message: \"CSS filters are unsupported in most email clients — remove them\",\n },\n {\n match: \"truncate\",\n message: \"text truncation is unsupported in Outlook for Windows — shorten the copy\",\n },\n {\n match: \"line-clamp-\",\n message: \"line clamping is unsupported in email clients — shorten the copy\",\n },\n {\n match: \"aspect-\",\n message: \"aspect-ratio is unsupported in Outlook and Gmail — set width/height on <Image>\",\n },\n {\n match: \"overflow-\",\n message: \"overflow is unreliable across email clients — restructure the layout\",\n },\n];\n\n/** Every root the resolver may try to split on, longest-match first (candidate.ts). */\nexport const registryRoots: ReadonlySet<string> = new Set(\n utilityRegistry.map((family) => family.root),\n);\n"],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { o as SmlParseError } from "./diagnostics-oveE-laV.js";
2
+ import { C as SmlDocument } from "./ast-DAhnAvQN.js";
3
+ import { a as VariableKind, l as Variables } from "./variables-3NGLpNTB.js";
4
+ import { a as JsxResult } from "./jsx-runtime-fSxcvlx4.js";
5
+ import { Result } from "effect";
6
+ //#region src/render-template.d.ts
7
+ /**
8
+ * A template is a named function of the recording proxy returning the draft
9
+ * root. The JSX transform types every element expression as `JSX.Element`
10
+ * (`JsxResult`); a well-formed template returns an `<Email>` element, and a
11
+ * stray Fragment root is caught at render time.
12
+ */
13
+ type Template<T extends Record<string, unknown>> = (variables: Variables<T>) => JsxResult;
14
+ /** One entry of a template's inferred variable schema. */
15
+ interface TemplateVariable {
16
+ readonly name: string;
17
+ /** Kind evidence from usage: numeric comparison ⇒ number, string ⇒ string, else unknown. */
18
+ readonly kind: VariableKind;
19
+ /**
20
+ * `true` when the variable is referenced outside every `<If>`/`<Else>` subtree
21
+ * (always needed to render); `false` when it only appears inside conditional
22
+ * branches (needed only when that branch is selected).
23
+ */
24
+ readonly required: boolean;
25
+ }
26
+ interface RenderedTemplate {
27
+ readonly document: SmlDocument;
28
+ readonly source: string;
29
+ readonly variables: ReadonlyArray<TemplateVariable>;
30
+ }
31
+ /**
32
+ * Runs a template function and returns its canonical document, source, and
33
+ * inferred variable schema. Grammar violations in the constructed tree surface
34
+ * as a parse `SmlParseError` (Result failure) with spans into the printed
35
+ * source. The template function itself may throw (e.g. the coercion guard) —
36
+ * that is a programmer error in the user's build, so it propagates rather than
37
+ * becoming a diagnostic.
38
+ */
39
+ declare const renderTemplate: <T extends Record<string, unknown>>(template: Template<T>) => Result.Result<RenderedTemplate, SmlParseError>;
40
+ //#endregion
41
+ export { RenderedTemplate, Template, TemplateVariable, renderTemplate };
42
+ //# sourceMappingURL=render-template.d.ts.map
@@ -0,0 +1,74 @@
1
+ import { collectPredicateVariables, parsePredicate } from "./predicate.js";
2
+ import { isRootTag } from "./ast.js";
3
+ import { createVariablesProxy } from "./variables.js";
4
+ import { parseSml } from "./parser.js";
5
+ import { printSml } from "./printer.js";
6
+ import { Result } from "effect";
7
+ //#region src/render-template.ts
8
+ /**
9
+ * Runs a template function and returns its canonical document, source, and
10
+ * inferred variable schema. Grammar violations in the constructed tree surface
11
+ * as a parse `SmlParseError` (Result failure) with spans into the printed
12
+ * source. The template function itself may throw (e.g. the coercion guard) —
13
+ * that is a programmer error in the user's build, so it propagates rather than
14
+ * becoming a diagnostic.
15
+ */
16
+ const renderTemplate = (template) => {
17
+ const { proxy, recorder } = createVariablesProxy();
18
+ const root = template(proxy);
19
+ if (root.kind !== "element" || !isRootTag(root.tag)) throw new TypeError("a template must return a single channel root element (<Email>, <Sms>, or <WhatsApp>), not a Fragment or other node");
20
+ const source = printSml({
21
+ kind: "document",
22
+ root
23
+ });
24
+ const parsed = parseSml(source);
25
+ if (Result.isFailure(parsed)) return Result.fail(parsed.failure);
26
+ const document = parsed.success;
27
+ return Result.succeed({
28
+ document,
29
+ source,
30
+ variables: collectVariableSchema(document, recorder)
31
+ });
32
+ };
33
+ /**
34
+ * Merges the recorder's kind evidence with structural required-ness read off the
35
+ * parsed document. A name referenced anywhere outside an `<If>` subtree is
36
+ * required; names seen only inside conditional branches are optional.
37
+ */
38
+ const collectVariableSchema = (document, recorder) => {
39
+ const unconditional = /* @__PURE__ */ new Set();
40
+ const conditional = /* @__PURE__ */ new Set();
41
+ const kindOf = new Map(recorder.collected().map((variable) => [variable.name, variable.kind]));
42
+ const visit = (node, insideIf) => {
43
+ if (node.kind === "interpolation") {
44
+ (insideIf ? conditional : unconditional).add(node.name);
45
+ return;
46
+ }
47
+ if (node.kind === "text") return;
48
+ const nowInsideIf = insideIf || node.tag === "If";
49
+ for (const attribute of node.attributes) {
50
+ if (attribute.value.kind !== "string") continue;
51
+ if (node.tag === "If" && attribute.name === "test") {
52
+ const parsed = parsePredicate(attribute.value.value, 0);
53
+ if (Result.isSuccess(parsed)) for (const name of collectPredicateVariables(parsed.success)) (insideIf ? conditional : unconditional).add(name);
54
+ continue;
55
+ }
56
+ for (const match of attribute.value.value.matchAll(INTERPOLATION_PATTERN)) {
57
+ const name = match[1] ?? match[2];
58
+ if (name !== void 0) (nowInsideIf ? conditional : unconditional).add(name);
59
+ }
60
+ }
61
+ for (const child of node.children) visit(child, nowInsideIf);
62
+ };
63
+ visit(document.root, false);
64
+ return [.../* @__PURE__ */ new Set([...unconditional, ...conditional])].map((name) => ({
65
+ name,
66
+ kind: kindOf.get(name) ?? "unknown",
67
+ required: unconditional.has(name)
68
+ }));
69
+ };
70
+ const INTERPOLATION_PATTERN = /\$\{\s*\/?([\w.]+)\s*\}|\{\{\s*([\w.]+)\s*\}\}/g;
71
+ //#endregion
72
+ export { renderTemplate };
73
+
74
+ //# sourceMappingURL=render-template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-template.js","names":[],"sources":["../src/render-template.ts"],"sourcesContent":["import { Result } from \"effect\";\n\nimport type { ElementNode, SmlDocument, SmlNode } from \"./ast\";\nimport { isRootTag } from \"./ast\";\nimport type { SmlParseError } from \"./diagnostics\";\nimport type { JsxResult } from \"./jsx-runtime\";\nimport { parseSml } from \"./parser\";\nimport { collectPredicateVariables, parsePredicate } from \"./predicate\";\nimport { printSml } from \"./printer\";\nimport type { VariableKind } from \"./variables\";\nimport { createVariablesProxy, VariableRecorder } from \"./variables\";\n\n// renderTemplate — the emit boundary for the TSX authoring layer (design brief\n// §7b). It invokes a named template function with the recording proxy, turns the\n// constructed draft tree into canonical SML source, then parses that source back:\n//\n// fn(proxy) → draft <Email> → printSml → source → parseSml → SmlDocument\n//\n// Parsing the printed source is the whole validation strategy: the parser is the\n// single source of grammatical truth (content models, attribute types/bounds,\n// predicate grammar, node-id assignment). A draft that violates the grammar —\n// text where only blocks are allowed, an out-of-range Heading level, an <Else>\n// that isn't last — fails here with the same span-precise diagnostics an agent\n// or dashboard user would get, so the two authoring paths cannot diverge.\n//\n// Node ids are therefore PARSE-assigned (document order, \"n0\"…): identical\n// template output yields identical ids, matching what the dashboard/canvas and\n// compiler see for the equivalent hand-authored .samva.\n\n/**\n * A template is a named function of the recording proxy returning the draft\n * root. The JSX transform types every element expression as `JSX.Element`\n * (`JsxResult`); a well-formed template returns an `<Email>` element, and a\n * stray Fragment root is caught at render time.\n */\nexport type Template<T extends Record<string, unknown>> = (\n variables: import(\"./variables\").Variables<T>,\n) => JsxResult;\n\n/** One entry of a template's inferred variable schema. */\nexport interface TemplateVariable {\n readonly name: string;\n /** Kind evidence from usage: numeric comparison ⇒ number, string ⇒ string, else unknown. */\n readonly kind: VariableKind;\n /**\n * `true` when the variable is referenced outside every `<If>`/`<Else>` subtree\n * (always needed to render); `false` when it only appears inside conditional\n * branches (needed only when that branch is selected).\n */\n readonly required: boolean;\n}\n\nexport interface RenderedTemplate {\n readonly document: SmlDocument;\n readonly source: string;\n readonly variables: ReadonlyArray<TemplateVariable>;\n}\n\n/**\n * Runs a template function and returns its canonical document, source, and\n * inferred variable schema. Grammar violations in the constructed tree surface\n * as a parse `SmlParseError` (Result failure) with spans into the printed\n * source. The template function itself may throw (e.g. the coercion guard) —\n * that is a programmer error in the user's build, so it propagates rather than\n * becoming a diagnostic.\n */\nexport const renderTemplate = <T extends Record<string, unknown>>(\n template: Template<T>,\n): Result.Result<RenderedTemplate, SmlParseError> => {\n const { proxy, recorder } = createVariablesProxy<T>();\n const root = template(proxy);\n if (root.kind !== \"element\" || !isRootTag(root.tag)) {\n // oxlint-disable-next-line samva/no-try-catch-or-throw, samva/no-error-constructor -- User-build authoring boundary: a non-root template result is a programmer error that must fail the user's TSX build loudly, not become a typed Effect failure.\n throw new TypeError(\n \"a template must return a single channel root element (<Email>, <Sms>, or <WhatsApp>), not a Fragment or other node\",\n );\n }\n const source = printSml({ kind: \"document\", root: root satisfies ElementNode });\n const parsed = parseSml(source);\n if (Result.isFailure(parsed)) return Result.fail(parsed.failure);\n const document = parsed.success;\n return Result.succeed({\n document,\n source,\n variables: collectVariableSchema(document, recorder),\n });\n};\n\n/**\n * Merges the recorder's kind evidence with structural required-ness read off the\n * parsed document. A name referenced anywhere outside an `<If>` subtree is\n * required; names seen only inside conditional branches are optional.\n */\nconst collectVariableSchema = (\n document: SmlDocument,\n recorder: VariableRecorder,\n): ReadonlyArray<TemplateVariable> => {\n const unconditional = new Set<string>();\n const conditional = new Set<string>();\n const kindOf = new Map(recorder.collected().map((variable) => [variable.name, variable.kind]));\n\n const visit = (node: SmlNode, insideIf: boolean): void => {\n if (node.kind === \"interpolation\") {\n (insideIf ? conditional : unconditional).add(node.name);\n return;\n }\n if (node.kind === \"text\") return;\n const nowInsideIf = insideIf || node.tag === \"If\";\n for (const attribute of node.attributes) {\n if (attribute.value.kind !== \"string\") continue;\n // An <If test> predicate is evaluated whenever the If itself is reached,\n // so its variables take the If's OWN position: required at top level,\n // conditional only when this If is nested inside another branch.\n if (node.tag === \"If\" && attribute.name === \"test\") {\n const parsed = parsePredicate(attribute.value.value, 0);\n if (Result.isSuccess(parsed)) {\n for (const name of collectPredicateVariables(parsed.success)) {\n (insideIf ? conditional : unconditional).add(name);\n }\n }\n continue;\n }\n for (const match of attribute.value.value.matchAll(INTERPOLATION_PATTERN)) {\n const name = match[1] ?? match[2];\n if (name !== undefined) (nowInsideIf ? conditional : unconditional).add(name);\n }\n }\n for (const child of node.children) visit(child, nowInsideIf);\n };\n visit(document.root, false);\n\n // Names come from the document itself (interpolations + predicates) — the\n // recorder only contributes KIND evidence, so a stray proxy access can't\n // mint a schema entry the template never uses.\n const names = new Set<string>([...unconditional, ...conditional]);\n return [...names].map((name) => ({\n name,\n kind: kindOf.get(name) ?? \"unknown\",\n required: unconditional.has(name),\n }));\n};\n\nconst INTERPOLATION_PATTERN = /\\$\\{\\s*\\/?([\\w.]+)\\s*\\}|\\{\\{\\s*([\\w.]+)\\s*\\}\\}/g;\n"],"mappings":";;;;;;;;;;;;;;;AAkEA,MAAa,kBACX,aACmD;CACnD,MAAM,EAAE,OAAO,aAAa,qBAAwB;CACpD,MAAM,OAAO,SAAS,KAAK;CAC3B,IAAI,KAAK,SAAS,aAAa,CAAC,UAAU,KAAK,GAAG,GAEhD,MAAM,IAAI,UACR,oHACF;CAEF,MAAM,SAAS,SAAS;EAAE,MAAM;EAAkB;CAA2B,CAAC;CAC9E,MAAM,SAAS,SAAS,MAAM;CAC9B,IAAI,OAAO,UAAU,MAAM,GAAG,OAAO,OAAO,KAAK,OAAO,OAAO;CAC/D,MAAM,WAAW,OAAO;CACxB,OAAO,OAAO,QAAQ;EACpB;EACA;EACA,WAAW,sBAAsB,UAAU,QAAQ;CACrD,CAAC;AACH;;;;;;AAOA,MAAM,yBACJ,UACA,aACoC;CACpC,MAAM,gCAAgB,IAAI,IAAY;CACtC,MAAM,8BAAc,IAAI,IAAY;CACpC,MAAM,SAAS,IAAI,IAAI,SAAS,UAAU,CAAC,CAAC,KAAK,aAAa,CAAC,SAAS,MAAM,SAAS,IAAI,CAAC,CAAC;CAE7F,MAAM,SAAS,MAAe,aAA4B;EACxD,IAAI,KAAK,SAAS,iBAAiB;GACjC,CAAC,WAAW,cAAc,cAAA,CAAe,IAAI,KAAK,IAAI;GACtD;EACF;EACA,IAAI,KAAK,SAAS,QAAQ;EAC1B,MAAM,cAAc,YAAY,KAAK,QAAQ;EAC7C,KAAK,MAAM,aAAa,KAAK,YAAY;GACvC,IAAI,UAAU,MAAM,SAAS,UAAU;GAIvC,IAAI,KAAK,QAAQ,QAAQ,UAAU,SAAS,QAAQ;IAClD,MAAM,SAAS,eAAe,UAAU,MAAM,OAAO,CAAC;IACtD,IAAI,OAAO,UAAU,MAAM,GACzB,KAAK,MAAM,QAAQ,0BAA0B,OAAO,OAAO,GACzD,CAAC,WAAW,cAAc,cAAA,CAAe,IAAI,IAAI;IAGrD;GACF;GACA,KAAK,MAAM,SAAS,UAAU,MAAM,MAAM,SAAS,qBAAqB,GAAG;IACzE,MAAM,OAAO,MAAM,MAAM,MAAM;IAC/B,IAAI,SAAS,KAAA,GAAW,CAAC,cAAc,cAAc,cAAA,CAAe,IAAI,IAAI;GAC9E;EACF;EACA,KAAK,MAAM,SAAS,KAAK,UAAU,MAAM,OAAO,WAAW;CAC7D;CACA,MAAM,SAAS,MAAM,KAAK;CAM1B,OAAO,CAAC,mBAAG,IADO,IAAY,CAAC,GAAG,eAAe,GAAG,WAAW,CAChD,CAAC,CAAC,CAAC,KAAK,UAAU;EAC/B;EACA,MAAM,OAAO,IAAI,IAAI,KAAK;EAC1B,UAAU,cAAc,IAAI,IAAI;CAClC,EAAE;AACJ;AAEA,MAAM,wBAAwB"}
@@ -0,0 +1,13 @@
1
+ //#region src/sms-segments.d.ts
2
+ /** Lowercase per the CompiledSms union (document's "GSM-7"/"UCS-2" renamed). */
3
+ type SmsEncoding = "gsm7" | "ucs2";
4
+ interface SmsSegmentInfo {
5
+ readonly encoding: SmsEncoding;
6
+ readonly characterCount: number;
7
+ readonly segmentCount: number;
8
+ }
9
+ declare const smsSegmentInfo: (text: string) => SmsSegmentInfo;
10
+ declare const hasSmsOptOut: (text: string) => boolean;
11
+ //#endregion
12
+ export { smsSegmentInfo as i, SmsSegmentInfo as n, hasSmsOptOut as r, SmsEncoding as t };
13
+ //# sourceMappingURL=sms-segments-y4FpPilJ.d.ts.map
@@ -0,0 +1,2 @@
1
+ import { i as smsSegmentInfo, n as SmsSegmentInfo, r as hasSmsOptOut, t as SmsEncoding } from "./sms-segments-y4FpPilJ.js";
2
+ export { SmsEncoding, SmsSegmentInfo, hasSmsOptOut, smsSegmentInfo };
@@ -0,0 +1,46 @@
1
+ //#region src/sms-segments.ts
2
+ const GSM7_EXTENDED = /* @__PURE__ */ new Set([
3
+ "[",
4
+ "]",
5
+ "{",
6
+ "}",
7
+ "\\",
8
+ "~",
9
+ "|",
10
+ "€",
11
+ "^",
12
+ "\f"
13
+ ]);
14
+ const GSM7_BASIC_PATTERN = /^[@£$¥èéùìòÇ\nØø\rÅåΔ_ΦΓΛΩΠΨΣΘΞÆæßÉ !"#¤%&'()*+,\-./:;<=>?¡0-9ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà]*$/;
15
+ const containsNonGsmCharacters = (text) => {
16
+ for (const char of text) {
17
+ if (GSM7_EXTENDED.has(char)) continue;
18
+ if (!GSM7_BASIC_PATTERN.test(char)) return true;
19
+ }
20
+ return false;
21
+ };
22
+ const smsSegmentInfo = (text) => {
23
+ if (containsNonGsmCharacters(text)) {
24
+ const codePoints = [...text].length;
25
+ const segmentCount = codePoints <= 70 ? 1 : Math.ceil(codePoints / 67);
26
+ return {
27
+ encoding: "ucs2",
28
+ characterCount: text.length,
29
+ segmentCount
30
+ };
31
+ }
32
+ let encodedLength = 0;
33
+ for (const char of text) encodedLength += GSM7_EXTENDED.has(char) ? 2 : 1;
34
+ const segmentCount = encodedLength <= 160 ? 1 : Math.ceil(encodedLength / 153);
35
+ return {
36
+ encoding: "gsm7",
37
+ characterCount: text.length,
38
+ segmentCount
39
+ };
40
+ };
41
+ const SMS_OPT_OUT_PATTERN = /\b(stop|unsubscribe|opt[\s-]?out|cancel|quit)\b/i;
42
+ const hasSmsOptOut = (text) => SMS_OPT_OUT_PATTERN.test(text);
43
+ //#endregion
44
+ export { hasSmsOptOut, smsSegmentInfo };
45
+
46
+ //# sourceMappingURL=sms-segments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sms-segments.js","names":[],"sources":["../src/sms-segments.ts"],"sourcesContent":["// SMS segment math, GSM 03.38. Ported from @samva/document channel-checks so\n// the compile backend (and later the dashboard character counter) share one\n// implementation; @samva/document is deleted in mk3.\n// GSM-7: 160 septets single / 153 multi-segment (UDH uses 7). Extended GSM-7\n// characters cost 2 septets. UCS-2 (any non-GSM char): 70 single / 67 multi.\n// @see https://en.wikipedia.org/wiki/GSM_03.38\n\nconst GSM7_EXTENDED = new Set([\"[\", \"]\", \"{\", \"}\", \"\\\\\", \"~\", \"|\", \"€\", \"^\", \"\\f\"]);\n\nconst GSM7_BASIC_PATTERN =\n /^[@£$¥èéùìòÇ\\nØø\\rÅåΔ_ΦΓΛΩΠΨΣΘΞÆæßÉ !\"#¤%&'()*+,\\-./:;<=>?¡0-9ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà]*$/;\n\nconst containsNonGsmCharacters = (text: string): boolean => {\n for (const char of text) {\n if (GSM7_EXTENDED.has(char)) continue;\n if (!GSM7_BASIC_PATTERN.test(char)) return true;\n }\n return false;\n};\n\n/** Lowercase per the CompiledSms union (document's \"GSM-7\"/\"UCS-2\" renamed). */\nexport type SmsEncoding = \"gsm7\" | \"ucs2\";\n\nexport interface SmsSegmentInfo {\n readonly encoding: SmsEncoding;\n readonly characterCount: number;\n readonly segmentCount: number;\n}\n\nexport const smsSegmentInfo = (text: string): SmsSegmentInfo => {\n if (containsNonGsmCharacters(text)) {\n const codePoints = [...text].length;\n const segmentCount = codePoints <= 70 ? 1 : Math.ceil(codePoints / 67);\n return { encoding: \"ucs2\", characterCount: text.length, segmentCount };\n }\n\n let encodedLength = 0;\n for (const char of text) {\n encodedLength += GSM7_EXTENDED.has(char) ? 2 : 1;\n }\n const segmentCount = encodedLength <= 160 ? 1 : Math.ceil(encodedLength / 153);\n return { encoding: \"gsm7\", characterCount: text.length, segmentCount };\n};\n\n// Recognizes \"STOP\", \"reply STOP\", \"text STOP to ...\", \"STOP to cancel\", etc.\nconst SMS_OPT_OUT_PATTERN = /\\b(stop|unsubscribe|opt[\\s-]?out|cancel|quit)\\b/i;\n\nexport const hasSmsOptOut = (text: string): boolean => SMS_OPT_OUT_PATTERN.test(text);\n"],"mappings":";AAOA,MAAM,gCAAgB,IAAI,IAAI;CAAC;CAAK;CAAK;CAAK;CAAK;CAAM;CAAK;CAAK;CAAK;CAAK;AAAI,CAAC;AAElF,MAAM,qBACJ;AAEF,MAAM,4BAA4B,SAA0B;CAC1D,KAAK,MAAM,QAAQ,MAAM;EACvB,IAAI,cAAc,IAAI,IAAI,GAAG;EAC7B,IAAI,CAAC,mBAAmB,KAAK,IAAI,GAAG,OAAO;CAC7C;CACA,OAAO;AACT;AAWA,MAAa,kBAAkB,SAAiC;CAC9D,IAAI,yBAAyB,IAAI,GAAG;EAClC,MAAM,aAAa,CAAC,GAAG,IAAI,CAAC,CAAC;EAC7B,MAAM,eAAe,cAAc,KAAK,IAAI,KAAK,KAAK,aAAa,EAAE;EACrE,OAAO;GAAE,UAAU;GAAQ,gBAAgB,KAAK;GAAQ;EAAa;CACvE;CAEA,IAAI,gBAAgB;CACpB,KAAK,MAAM,QAAQ,MACjB,iBAAiB,cAAc,IAAI,IAAI,IAAI,IAAI;CAEjD,MAAM,eAAe,iBAAiB,MAAM,IAAI,KAAK,KAAK,gBAAgB,GAAG;CAC7E,OAAO;EAAE,UAAU;EAAQ,gBAAgB,KAAK;EAAQ;CAAa;AACvE;AAGA,MAAM,sBAAsB;AAE5B,MAAa,gBAAgB,SAA0B,oBAAoB,KAAK,IAAI"}
@@ -0,0 +1,116 @@
1
+ //#region src/theme.d.ts
2
+ interface TextToken {
3
+ readonly sizePx: number;
4
+ readonly lineHeightPx: number;
5
+ }
6
+ /** One hosted @font-face: the served woff2 src + its weight/style, and the
7
+ * css2 unicode-range (absent for a whole-file upload). */
8
+ interface FontFace {
9
+ readonly weight: number;
10
+ readonly style: "normal" | "italic";
11
+ readonly src: string;
12
+ readonly unicodeRange?: string | undefined;
13
+ }
14
+ /** The @font-face set for one font token: the family the rules declare (and that
15
+ * the token's inline stack leads with) plus its hosted faces. */
16
+ interface FontFaceSet {
17
+ readonly family: string;
18
+ readonly faces: ReadonlyArray<FontFace>;
19
+ }
20
+ /**
21
+ * Escape a value interpolated inside a single-quoted CSS string — a
22
+ * `font-family` name or a `url('…')` src. A raw `'` closes the quote early (for
23
+ * a src that also lets a crafted value escape the `url()`), and a raw `\` is read
24
+ * as a CSS escape sequence. `family` and `src` are only min-length-validated
25
+ * upstream, so escaping both keeps every emitted CSS string grammar-safe. Used
26
+ * both here (the inline `font-{name}` stack) and by the compiler's @font-face
27
+ * block, so an escapable family never lands unescaped in either place.
28
+ *
29
+ * The `@font-face` block lives inside a `<style>` element, and an HTML parser
30
+ * scans raw-text elements for the literal `</style>` end tag BEFORE any CSS
31
+ * parsing. So a crafted `family`/`src` containing `</style>` would close the
32
+ * style element and let the remainder render as HTML in the delivered email
33
+ * (a stored-XSS breakout). Rewriting `</` to `<\/` neutralises this: in a
34
+ * quoted CSS string `\/` is identical to `/`, but the HTML tokenizer no longer
35
+ * matches `<\/style>` as a raw-text end tag.
36
+ */
37
+ declare const escapeCssString: (value: string) => string;
38
+ /** A persisted font token: the family the inline stack leads with, the fallback
39
+ * appended after it, and (for a self-hosted webfont) the hosted faces. The
40
+ * public compiler owns the single flatten transform for this shape. */
41
+ interface FontToken {
42
+ readonly family: string;
43
+ readonly fallback: string;
44
+ readonly faces?: ReadonlyArray<FontFace> | undefined;
45
+ }
46
+ /**
47
+ * Flatten persisted font tokens into the compiler's two font fields, so the
48
+ * server (apps/api) and the in-browser canvas (apps/web) produce byte-identical
49
+ * output from ONE transform rather than two hand-synced copies:
50
+ * - `fontFamilies` = the inline stack the `font-{name}` utility emits — the
51
+ * family quoted (multi-word-safe) then the fallback (which the picker
52
+ * guarantees ends in a generic, so no client is ever fontless).
53
+ * - `fontFaces` = the @font-face set for tokens that self-host a webfont.
54
+ * Tokens without `faces` (system stacks) contribute a family but no face set.
55
+ */
56
+ declare const flattenFontTokens: (tokens: Readonly<Record<string, FontToken>>) => {
57
+ fontFamilies: Record<string, string>;
58
+ fontFaces: Record<string, FontFaceSet>;
59
+ };
60
+ interface TokenTable {
61
+ /** Flattened color names ("red-500", "white", "brand") → 6-digit lowercase hex. */
62
+ readonly colors: Readonly<Record<string, string>>;
63
+ /**
64
+ * Dark counterparts, colors namespace only (dark radius/fonts are not a
65
+ * thing). Only tokens with a dark pair appear; a color-family utility that
66
+ * resolves a paired token also emits the dark head rule automatically.
67
+ */
68
+ readonly colorsDark: Readonly<Record<string, string>>;
69
+ /** px per spacing unit (`p-4` → 4 × spacingPx). */
70
+ readonly spacingPx: number;
71
+ /** text-{size} → font-size / line-height px pair. */
72
+ readonly text: Readonly<Record<string, TextToken>>;
73
+ /** leading-{name} → unitless line-height ratio. */
74
+ readonly leading: Readonly<Record<string, number>>;
75
+ /** tracking-{name} → letter-spacing em. */
76
+ readonly trackingEm: Readonly<Record<string, number>>;
77
+ /** rounded-{name} → border-radius px. */
78
+ readonly radiusPx: Readonly<Record<string, number>>;
79
+ /** font-{name} → font-weight number. */
80
+ readonly fontWeights: Readonly<Record<string, number>>;
81
+ /** font-{name} → the resolved inline font-family stack (family + fallback,
82
+ * guaranteed to end in a generic). */
83
+ readonly fontFamilies: Readonly<Record<string, string>>;
84
+ /** font-{name} → its @font-face set, for tokens that self-host a webfont.
85
+ * Tokens with no faces (system stacks) are absent. */
86
+ readonly fontFaces: Readonly<Record<string, FontFaceSet>>;
87
+ /** max-w-{name} → px. */
88
+ readonly containerPx: Readonly<Record<string, number>>;
89
+ }
90
+ interface TokenOverrides {
91
+ readonly colors?: Readonly<Record<string, string>> | undefined;
92
+ readonly colorsDark?: Readonly<Record<string, string>> | undefined;
93
+ readonly spacingPx?: number | undefined;
94
+ readonly text?: Readonly<Record<string, TextToken>> | undefined;
95
+ readonly leading?: Readonly<Record<string, number>> | undefined;
96
+ readonly trackingEm?: Readonly<Record<string, number>> | undefined;
97
+ readonly radiusPx?: Readonly<Record<string, number>> | undefined;
98
+ readonly fontWeights?: Readonly<Record<string, number>> | undefined;
99
+ readonly fontFamilies?: Readonly<Record<string, string>> | undefined;
100
+ readonly fontFaces?: Readonly<Record<string, FontFaceSet>> | undefined;
101
+ readonly containerPx?: Readonly<Record<string, number>> | undefined;
102
+ }
103
+ interface ThemeLayers {
104
+ readonly base: TokenTable;
105
+ /**
106
+ * Ordered token-override layers, least → most specific (organization →
107
+ * domain → template). Each is merged onto the running table in turn, so a
108
+ * later layer's token wins. The caller (the settings cascade in apps/api)
109
+ * owns assembling this list from the effective design.
110
+ */
111
+ readonly overrides?: ReadonlyArray<TokenOverrides> | undefined;
112
+ }
113
+ declare const resolveTokenTable: (layers: ThemeLayers) => TokenTable;
114
+ //#endregion
115
+ export { ThemeLayers as a, escapeCssString as c, TextToken as i, flattenFontTokens as l, FontFaceSet as n, TokenOverrides as o, FontToken as r, TokenTable as s, FontFace as t, resolveTokenTable as u };
116
+ //# sourceMappingURL=theme-6ApjjiBo.d.ts.map
@@ -0,0 +1,24 @@
1
+ import { t as Diagnostic } from "./diagnostics-oveE-laV.js";
2
+ import { o as TokenOverrides } from "./theme-6ApjjiBo.js";
3
+ import { Result } from "effect";
4
+ //#region src/theme-css.d.ts
5
+ /**
6
+ * `TokenOverrides` plus `tracking`. The compiler's `TokenOverrides` names its
7
+ * letter-spacing slot `trackingEm`; the `@theme` CSS namespace and the persisted
8
+ * theme schema both call it `tracking`, so the parser surfaces it under that
9
+ * name and apps/api maps `tracking → trackingEm` at the compile call sites.
10
+ */
11
+ interface ParsedTheme extends TokenOverrides {
12
+ /** tracking-{name} → letter-spacing in em (negative allowed). */
13
+ readonly tracking?: Readonly<Record<string, number>> | undefined;
14
+ }
15
+ declare const parseThemeCss: (css: string) => Result.Result<ParsedTheme, ReadonlyArray<Diagnostic>>;
16
+ /**
17
+ * Serialize a ParsedTheme to `@theme` CSS. Light tokens go in the top-level
18
+ * `@theme` block; `colorsDark` (colors-only, matching the parser) goes in the
19
+ * `@media (prefers-color-scheme: dark) { @theme { … } }` wrapper.
20
+ */
21
+ declare const printThemeCss: (theme: ParsedTheme) => string;
22
+ //#endregion
23
+ export { ParsedTheme, parseThemeCss, printThemeCss };
24
+ //# sourceMappingURL=theme-css.d.ts.map