@samva/markup 0.4.0 → 0.6.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 (107) hide show
  1. package/AGENTS.md +19 -0
  2. package/dist/{ast-63I-VLQx.d.ts → ast-BrDtApwS.d.ts} +34 -60
  3. package/dist/ast.d.ts +2 -2
  4. package/dist/ast.js +4 -52
  5. package/dist/{catalog-Ck1tAGwO.d.ts → catalog-60eaYC7b.d.ts} +2 -2
  6. package/dist/{checks-DXuERO4B.js → checks-DBweSIPN.js} +18 -20
  7. package/dist/checks.d.ts +1 -1
  8. package/dist/checks.js +1 -1
  9. package/dist/classes.d.ts +1 -1
  10. package/dist/compile-sms.d.ts +3 -3
  11. package/dist/compile-whatsapp.d.ts +5 -5
  12. package/dist/compile-whatsapp.js +3 -3
  13. package/dist/compile.d.ts +5 -5
  14. package/dist/compile.js +5 -5
  15. package/dist/compiler.d.ts +8 -5
  16. package/dist/compiler.js +45 -45
  17. package/dist/components.d.ts +4 -4
  18. package/dist/conditionals.js +56 -24
  19. package/dist/corpus-BjYMqOoY.d.ts +106 -0
  20. package/dist/{css-ar2lU3NC.d.ts → css-COcFOT1y.d.ts} +4 -5
  21. package/dist/diagnostics-BMYtBCQk.d.ts +46 -0
  22. package/dist/diagnostics-OcQKQYSY.d.ts +49 -0
  23. package/dist/diagnostics.d.ts +2 -2
  24. package/dist/diagnostics.js +24 -33
  25. package/dist/{elements-DtwKlFVH.d.ts → elements-BzjjnoCS.d.ts} +40 -161
  26. package/dist/email/components.d.ts +1 -1
  27. package/dist/email/components.js +40 -10
  28. package/dist/email/diagnostics.d.ts +1 -1
  29. package/dist/email/diagnostics.js +8 -43
  30. package/dist/email/elements.d.ts +1 -1
  31. package/dist/email/html.d.ts +2 -2
  32. package/dist/email/html.js +11 -10
  33. package/dist/email/jsx-dev-runtime.d.ts +3 -21
  34. package/dist/email/jsx-runtime.d.ts +1 -1
  35. package/dist/email/jsx-runtime.js +29 -3
  36. package/dist/email/render.d.ts +4 -4
  37. package/dist/email/render.js +1 -1
  38. package/dist/email/selection.d.ts +20 -31
  39. package/dist/email/selection.js +1 -53
  40. package/dist/email/source-edit.d.ts +1 -1
  41. package/dist/email/source-edit.js +2 -2
  42. package/dist/email/stylesheets.d.ts +1 -1
  43. package/dist/email/text.d.ts +1 -1
  44. package/dist/email/text.js +3 -3
  45. package/dist/emit.d.ts +2 -2
  46. package/dist/emit.js +5 -5
  47. package/dist/footer.js +7 -4
  48. package/dist/guards-eV1qLbf2.js +9 -0
  49. package/dist/{input-schema-BIfzI63B.d.ts → input-schema-7dPFVvNE.d.ts} +2 -2
  50. package/dist/input-schema-zjkgrXn2.js +455 -0
  51. package/dist/input-schema.d.ts +1 -1
  52. package/dist/input-schema.js +1 -169
  53. package/dist/interpolate.d.ts +9 -12
  54. package/dist/interpolate.js +1 -19
  55. package/dist/jsx-dev-runtime.d.ts +2 -2
  56. package/dist/{jsx-runtime-C5k_Zq0A.d.ts → jsx-runtime-I-1nCtl2.d.ts} +3 -3
  57. package/dist/jsx-runtime.d.ts +1 -1
  58. package/dist/jsx-runtime.js +3 -3
  59. package/dist/lexer.js +17 -5
  60. package/dist/model.d.ts +18 -33
  61. package/dist/model.js +4 -17
  62. package/dist/ops-2aiiYO7m.js +630 -0
  63. package/dist/ops.d.ts +78 -172
  64. package/dist/ops.js +2 -546
  65. package/dist/{parser-CGBS73Gz.d.ts → parser-CQcASEyy.d.ts} +5 -5
  66. package/dist/parser.d.ts +1 -1
  67. package/dist/parser.js +8 -8
  68. package/dist/{predicate-CvkzT-a2.js → predicate-BI6uRRM-.js} +24 -14
  69. package/dist/{predicate-G2SeSvoN.d.ts → predicate-M-05Nd7R.d.ts} +4 -4
  70. package/dist/predicate.d.ts +1 -1
  71. package/dist/predicate.js +1 -1
  72. package/dist/presets/category-mapping.d.ts +1 -1
  73. package/dist/presets/corpus.d.ts +1 -1
  74. package/dist/presets/corpus.js +74 -117
  75. package/dist/presets/public-gallery.d.ts +2 -2
  76. package/dist/presets/public-gallery.js +17 -9
  77. package/dist/presets/public-handoff.d.ts +4 -4
  78. package/dist/presets/public-handoff.js +10 -9
  79. package/dist/presets/public-preview-artifacts.d.ts +7 -5
  80. package/dist/presets/public-preview-artifacts.js +10 -9
  81. package/dist/presets/showcase.d.ts +1 -1
  82. package/dist/presets.d.ts +1 -1
  83. package/dist/printer.d.ts +1 -1
  84. package/dist/{render-ykQhtMs_.js → render-aaJBnsXK.js} +44 -27
  85. package/dist/render-template.d.ts +7 -7
  86. package/dist/render-template.js +6 -6
  87. package/dist/{showcase-C_xgl6hz.d.ts → showcase-DGZAWn4Z.d.ts} +2 -2
  88. package/dist/{source-locations-BCJQnIfU.d.ts → source-locations-DrCM82Ym.d.ts} +4 -26
  89. package/dist/source-locations.d.ts +2 -2
  90. package/dist/source-locations.js +3 -26
  91. package/dist/{template-Dy5yOPyp.d.ts → template-DWftqh4x.d.ts} +4 -4
  92. package/dist/template.d.ts +1 -1
  93. package/dist/template.js +3 -3
  94. package/dist/theme-css.d.ts +3 -3
  95. package/dist/theme-css.js +3 -3
  96. package/dist/theme.js +25 -2
  97. package/dist/tsx-source-instrumentation.d.ts +1 -3
  98. package/dist/tsx-source-instrumentation.js +6 -6
  99. package/dist/tsx-tracer.d.ts +7 -11
  100. package/dist/tsx-tracer.js +165 -165
  101. package/dist/{variables-if1VrV34.d.ts → variables-J_L3qxRa.d.ts} +2 -2
  102. package/dist/variables.d.ts +1 -1
  103. package/dist/variables.js +4 -4
  104. package/package.json +4 -3
  105. package/dist/corpus-U2EeALvS.d.ts +0 -234
  106. package/dist/diagnostics-Djd9ckLg.d.ts +0 -90
  107. package/dist/diagnostics-oveE-laV.d.ts +0 -79
@@ -3,7 +3,7 @@ import { registerPublicCorpusEntry } from "./corpus.js";
3
3
  import { publicTemplates } from "./public-gallery.js";
4
4
  import { presetEmailAssets, presetEmailFixtures, presetModuleSource } from "./sources.js";
5
5
  import { publicTemplatePreviews } from "./public-preview.js";
6
- import { Result } from "effect";
6
+ import { Result } from "better-result";
7
7
  //#region presets/public-preview-artifacts.ts
8
8
  const renderFrames = [
9
9
  {
@@ -50,15 +50,15 @@ const buildPublicTemplatePreviewArtifact = (template) => {
50
50
  corpus: template.corpus,
51
51
  facets: template.facets
52
52
  });
53
- if (Result.isFailure(registration)) return Result.fail({
53
+ if (Result.isError(registration)) return Result.err({
54
54
  presetId: template.presetId,
55
- message: `public corpus registration failed: ${registration.failure.message}`
55
+ message: `public corpus registration failed: ${registration.error.message}`
56
56
  });
57
57
  const source = presetModuleSource(template.presetId);
58
58
  const fixture = presetEmailFixtures[template.presetId]?.default;
59
59
  const preset = presetCatalog.find((entry) => entry.id === template.presetId);
60
60
  const preview = publicTemplatePreviews[template.presetId];
61
- if (source === void 0 || preset === void 0 || fixture === void 0) return Result.fail({
61
+ if (source === void 0 || preset === void 0 || fixture === void 0) return Result.err({
62
62
  presetId: template.presetId,
63
63
  message: "canonical preset source is missing"
64
64
  });
@@ -66,13 +66,13 @@ const buildPublicTemplatePreviewArtifact = (template) => {
66
66
  const assets = new Set(presetEmailAssets[template.presetId]);
67
67
  for (const asset of preview.assets) {
68
68
  const value = sampleData[asset.variable];
69
- if (typeof value !== "string") return Result.fail({
69
+ if (typeof value !== "string") return Result.err({
70
70
  presetId: template.presetId,
71
71
  message: `sample data is missing a string asset: ${asset.variable}`
72
72
  });
73
73
  assets.add(value);
74
74
  }
75
- return Result.succeed({
75
+ return Result.ok({
76
76
  presetId: template.presetId,
77
77
  slug: template.slug,
78
78
  subject: fixture.subject,
@@ -80,6 +80,7 @@ const buildPublicTemplatePreviewArtifact = (template) => {
80
80
  quality: preview.quality,
81
81
  source,
82
82
  html: fixture.html,
83
+ text: fixture.text,
83
84
  assets: [...assets],
84
85
  renders: renderInputs(template.slug, fixture.html)
85
86
  });
@@ -90,10 +91,10 @@ const buildPublicTemplatePreviewArtifactsFor = (templates) => {
90
91
  const issues = [];
91
92
  for (const template of templates) {
92
93
  const artifact = buildPublicTemplatePreviewArtifact(template);
93
- if (Result.isSuccess(artifact)) artifacts.push(artifact.success);
94
- else issues.push(artifact.failure);
94
+ if (Result.isOk(artifact)) artifacts.push(artifact.value);
95
+ else issues.push(artifact.error);
95
96
  }
96
- return issues.length > 0 ? Result.fail(issues) : Result.succeed(artifacts);
97
+ return issues.length > 0 ? Result.err(issues) : Result.ok(artifacts);
97
98
  };
98
99
  /** Build the complete canonical public gallery. */
99
100
  const buildPublicTemplatePreviewArtifacts = () => buildPublicTemplatePreviewArtifactsFor(publicTemplates);
@@ -1,2 +1,2 @@
1
- import { a as ShowcaseProjectId, c as showcaseProject, d as showcaseSourcePath, i as ShowcaseProject, l as showcaseProjectPaths, n as OwnedShowcaseId, o as ShowcaseProvenance, r as SHOWCASE_ROOT, s as isShowcaseProjectId, t as BrandRecreationShowcaseId, u as showcaseProjects } from "../showcase-C_xgl6hz.js";
1
+ import { a as ShowcaseProjectId, c as showcaseProject, d as showcaseSourcePath, i as ShowcaseProject, l as showcaseProjectPaths, n as OwnedShowcaseId, o as ShowcaseProvenance, r as SHOWCASE_ROOT, s as isShowcaseProjectId, t as BrandRecreationShowcaseId, u as showcaseProjects } from "../showcase-DGZAWn4Z.js";
2
2
  export { BrandRecreationShowcaseId, OwnedShowcaseId, SHOWCASE_ROOT, ShowcaseProject, ShowcaseProjectId, ShowcaseProvenance, isShowcaseProjectId, showcaseProject, showcaseProjectPaths, showcaseProjects, showcaseSourcePath };
package/dist/presets.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as PresetMeta, c as presetCatalog, i as PresetId, n as EmailPresetVariable, o as PresetVariable, r as PresetCategory, s as RECOMMENDED_THEME, t as EmailPresetId } from "./catalog-Ck1tAGwO.js";
1
+ import { a as PresetMeta, c as presetCatalog, i as PresetId, n as EmailPresetVariable, o as PresetVariable, r as PresetCategory, s as RECOMMENDED_THEME, t as EmailPresetId } from "./catalog-60eaYC7b.js";
2
2
  export { EmailPresetId, EmailPresetVariable, PresetCategory, PresetId, PresetMeta, PresetVariable, RECOMMENDED_THEME, presetCatalog };
package/dist/printer.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { w as SmlDocument } from "./ast-63I-VLQx.js";
1
+ import { b as SmlDocument } from "./ast-BrDtApwS.js";
2
2
  //#region src/printer.d.ts
3
3
  declare const printSml: (document: SmlDocument) => string;
4
4
  //#endregion
@@ -1,27 +1,38 @@
1
+ import { n as isObject } from "./guards-eV1qLbf2.js";
1
2
  import { EmailNode, drainEmailDiagnostics } from "./email/jsx-runtime.js";
2
3
  import { declarationValue, kebab, serializeEmailHtml } from "./email/html.js";
3
4
  import { PREHEADER_ATTRIBUTE, preheaderNode } from "./email/components.js";
4
5
  import { EmailCompileError, hasBlockingDiagnostic } from "./email/diagnostics.js";
5
6
  import { snapshotStylesheets } from "./email/stylesheets.js";
6
7
  import { deriveEmailText } from "./email/text.js";
7
- import { Predicate } from "effect";
8
8
  //#region src/email/css-values.ts
9
- /** Expand `var(--x, fallback)` against the collected custom properties. */
10
- const resolveVariables = (value, variables, depth = 0) => {
9
+ const MAX_RESOLVED_VALUE_LENGTH = 65536;
10
+ const MAX_RESOLUTION_WORK = 262144;
11
+ const resolveVariablesWithin = (value, variables, depth, budget, work) => {
12
+ if (value.length > work.remaining) return void 0;
13
+ work.remaining -= value.length;
14
+ if (value.length > budget) return void 0;
11
15
  if (depth > 8 || !value.includes("var(")) return value;
12
- let result = "";
13
- let rest = value;
14
- while (rest.length > 0) {
15
- const start = rest.indexOf("var(");
16
+ const chunks = [];
17
+ let length = 0;
18
+ const append = (chunk) => {
19
+ length += chunk.length;
20
+ if (length > budget) return false;
21
+ chunks.push(chunk);
22
+ return true;
23
+ };
24
+ let cursor = 0;
25
+ while (cursor < value.length) {
26
+ const start = value.indexOf("var(", cursor);
16
27
  if (start < 0) {
17
- result += rest;
28
+ if (!append(value.slice(cursor))) return void 0;
18
29
  break;
19
30
  }
20
- result += rest.slice(0, start);
31
+ if (!append(value.slice(cursor, start))) return void 0;
21
32
  let level = 0;
22
33
  let end = -1;
23
- for (let index = start + 3; index < rest.length; index++) if (rest[index] === "(") level += 1;
24
- else if (rest[index] === ")") {
34
+ for (let index = start + 3; index < value.length; index++) if (value[index] === "(") level += 1;
35
+ else if (value[index] === ")") {
25
36
  level -= 1;
26
37
  if (level === 0) {
27
38
  end = index;
@@ -29,20 +40,26 @@ const resolveVariables = (value, variables, depth = 0) => {
29
40
  }
30
41
  }
31
42
  if (end < 0) {
32
- result += rest.slice(start);
43
+ if (!append(value.slice(start))) return void 0;
33
44
  break;
34
45
  }
35
- const body = rest.slice(start + 4, end);
46
+ const body = value.slice(start + 4, end);
36
47
  const comma = body.indexOf(",");
37
48
  const name = (comma < 0 ? body : body.slice(0, comma)).trim();
38
49
  const fallback = comma < 0 ? void 0 : body.slice(comma + 1).trim();
39
50
  const replacement = variables[name] ?? fallback;
40
- result += replacement === void 0 ? rest.slice(start, end + 1) : resolveVariables(replacement, variables, depth + 1);
41
- rest = rest.slice(end + 1);
51
+ const resolved = replacement === void 0 ? value.slice(start, end + 1) : resolveVariablesWithin(replacement, variables, depth + 1, budget - length, work);
52
+ if (resolved === void 0 || !append(resolved)) return void 0;
53
+ cursor = end + 1;
42
54
  }
43
- return result;
55
+ return chunks.join("");
44
56
  };
45
57
  /**
58
+ * Expand `var(--x, fallback)` against the collected custom properties.
59
+ * Values whose expansion exceeds the render-time budget are left as authored.
60
+ */
61
+ const resolveVariables = (value, variables, depth = 0) => resolveVariablesWithin(value, variables, depth, MAX_RESOLVED_VALUE_LENGTH, { remaining: MAX_RESOLUTION_WORK }) ?? value;
62
+ /**
46
63
  * Fold a constant `calc()` to a plain value. Tailwind writes every spacing
47
64
  * utility as `calc(var(--spacing) * n)`, and classic Outlook does not implement
48
65
  * `calc()` at all, so an unfolded value is a silently missing style. An
@@ -276,7 +293,7 @@ const compareRank = (left, right) => {
276
293
  const outranks = (left, right) => compareRank(left, right) >= 0;
277
294
  /** An authored `style` object becomes declarations with the same normalization the serializer uses. */
278
295
  const inlineDeclarations = (style) => {
279
- if (!Predicate.isObject(style) || Array.isArray(style)) return [];
296
+ if (!isObject(style) || Array.isArray(style)) return [];
280
297
  const output = [];
281
298
  for (const [property, value] of Object.entries(style)) {
282
299
  if (value === void 0 || value === null) continue;
@@ -344,7 +361,7 @@ const applyStylesheet = (root, sheet, options = {}) => {
344
361
  const visitChildren = (children, ancestry, variables, writing) => {
345
362
  const previous = [];
346
363
  const visit = (nodes) => nodes.map((child) => {
347
- if (child._tag === "Element") {
364
+ if (child.type === "Element") {
348
365
  const result = applyTo(child, {
349
366
  element: child,
350
367
  parent: ancestry,
@@ -353,7 +370,7 @@ const applyStylesheet = (root, sheet, options = {}) => {
353
370
  previous.push(child);
354
371
  return result;
355
372
  }
356
- if (child._tag === "Fragment") return EmailNode.Fragment({ children: visit(child.children) });
373
+ if (child.type === "Fragment") return EmailNode.Fragment({ children: visit(child.children) });
357
374
  return child;
358
375
  });
359
376
  return visit(children);
@@ -419,11 +436,11 @@ const applyStylesheet = (root, sheet, options = {}) => {
419
436
  children: visitChildren(element.children, ancestry, variables, writing)
420
437
  });
421
438
  };
422
- const tree = root._tag === "Element" ? applyTo(root, {
439
+ const tree = root.type === "Element" ? applyTo(root, {
423
440
  element: root,
424
441
  parent: void 0,
425
442
  previous: []
426
- }, sheet.variables, horizontalWriting) : root._tag === "Fragment" ? EmailNode.Fragment({ children: visitChildren(root.children, void 0, sheet.variables, horizontalWriting) }) : root;
443
+ }, sheet.variables, horizontalWriting) : root.type === "Fragment" ? EmailNode.Fragment({ children: visitChildren(root.children, void 0, sheet.variables, horizontalWriting) }) : root;
427
444
  for (const [name, origins] of unmatched) diagnostics.push({
428
445
  code: "unmatched-class",
429
446
  severity: "warning",
@@ -457,7 +474,7 @@ const compileEmail = (node, options = {}) => {
457
474
  };
458
475
  /** The preview text already present in the tree, from `<Email preheader>`. */
459
476
  const declaredPreheader = (node) => {
460
- switch (node._tag) {
477
+ switch (node.type) {
461
478
  case "Text":
462
479
  case "Raw": return;
463
480
  case "Fragment": return node.children.reduce((found, child) => found ?? declaredPreheader(child), void 0);
@@ -471,14 +488,14 @@ const declaredPreheader = (node) => {
471
488
  /** Put the hidden preview block first inside `<body>`, or first overall when there is no shell. */
472
489
  const withPreheader = (node, text) => {
473
490
  const insert = (current) => {
474
- if (current._tag === "Fragment") {
491
+ if (current.type === "Fragment") {
475
492
  for (const [index, child] of current.children.entries()) {
476
493
  const replaced = insert(child);
477
494
  if (replaced !== void 0) return EmailNode.Fragment({ children: current.children.with(index, replaced) });
478
495
  }
479
496
  return;
480
497
  }
481
- if (current._tag !== "Element") return void 0;
498
+ if (current.type !== "Element") return void 0;
482
499
  if (current.tag === "body") return EmailNode.Element({
483
500
  ...current,
484
501
  children: [preheaderNode(text), ...current.children]
@@ -496,14 +513,14 @@ const withPreheader = (node, text) => {
496
513
  /** Put the retained rules in a `<style>` at the end of `<head>`. */
497
514
  const withHeadCss = (node, css) => {
498
515
  const insert = (current) => {
499
- if (current._tag === "Fragment") {
516
+ if (current.type === "Fragment") {
500
517
  for (const [index, child] of current.children.entries()) {
501
518
  const replaced = insert(child);
502
519
  if (replaced !== void 0) return EmailNode.Fragment({ children: current.children.with(index, replaced) });
503
520
  }
504
521
  return;
505
522
  }
506
- if (current._tag !== "Element") return void 0;
523
+ if (current.type !== "Element") return void 0;
507
524
  if (current.tag === "head") return EmailNode.Element({
508
525
  ...current,
509
526
  children: [...current.children, EmailNode.Element({
@@ -589,4 +606,4 @@ const renderEmailWith = (stylesheet, template, data, options) => {
589
606
  //#endregion
590
607
  export { applyStylesheet as a, downlevelMediaQuery as c, renderHeadCss as d, resolveVariables as f, renderEmail as i, finalizeValue as l, bindStylesheets as n, classListOf as o, compileEmail as r, matchesSelector as s, EMAIL_DOCTYPE as t, foldCalc as u };
591
608
 
592
- //# sourceMappingURL=render-ykQhtMs_.js.map
609
+ //# sourceMappingURL=render-aaJBnsXK.js.map
@@ -1,9 +1,9 @@
1
- import { o as SmlParseError } from "./diagnostics-oveE-laV.js";
2
- import { w as SmlDocument } from "./ast-63I-VLQx.js";
3
- import { c as UnboundTemplateSourceLocations } from "./source-locations-BCJQnIfU.js";
4
- import { a as VariableKind, l as Variables } from "./variables-if1VrV34.js";
5
- import { a as JsxResult } from "./jsx-runtime-C5k_Zq0A.js";
6
- import { Result } from "effect";
1
+ import { i as SmlParseError } from "./diagnostics-OcQKQYSY.js";
2
+ import { b as SmlDocument } from "./ast-BrDtApwS.js";
3
+ import { s as UnboundTemplateSourceLocations } from "./source-locations-DrCM82Ym.js";
4
+ import { a as VariableKind, l as Variables } from "./variables-J_L3qxRa.js";
5
+ import { a as JsxResult } from "./jsx-runtime-I-1nCtl2.js";
6
+ import { Result } from "better-result";
7
7
  //#region src/render-template.d.ts
8
8
  /**
9
9
  * A template is a named function of the recording proxy returning the draft
@@ -39,7 +39,7 @@ interface RenderedTemplate {
39
39
  * that is a programmer error in the user's build, so it propagates rather than
40
40
  * becoming a diagnostic.
41
41
  */
42
- declare const renderTemplate: <T extends Record<string, unknown>>(template: Template<T>) => Result.Result<RenderedTemplate, SmlParseError>;
42
+ declare const renderTemplate: <T extends Record<string, unknown>>(template: Template<T>) => Result<RenderedTemplate, SmlParseError>;
43
43
  //#endregion
44
44
  export { RenderedTemplate, Template, TemplateVariable, renderTemplate };
45
45
  //# sourceMappingURL=render-template.d.ts.map
@@ -1,10 +1,10 @@
1
1
  import { mapJsxSourceLocations } from "./source-locations.js";
2
- import { i as parsePredicate, n as collectPredicateVariables } from "./predicate-CvkzT-a2.js";
2
+ import { i as parsePredicate, n as collectPredicateVariables } from "./predicate-BI6uRRM-.js";
3
3
  import { isRootTag } from "./ast.js";
4
4
  import { createVariablesProxy } from "./variables.js";
5
5
  import { parseSml } from "./parser.js";
6
6
  import { printSml } from "./printer.js";
7
- import { Result } from "effect";
7
+ import { Result } from "better-result";
8
8
  //#region src/render-template.ts
9
9
  /**
10
10
  * Runs a template function and returns its canonical document, source, and
@@ -23,9 +23,9 @@ const renderTemplate = (template) => {
23
23
  root
24
24
  });
25
25
  const parsed = parseSml(source);
26
- if (Result.isFailure(parsed)) return Result.fail(parsed.failure);
27
- const document = parsed.success;
28
- return Result.succeed({
26
+ if (Result.isError(parsed)) return Result.err(parsed.error);
27
+ const document = parsed.value;
28
+ return Result.ok({
29
29
  document,
30
30
  source,
31
31
  variables: collectVariableSchema(document, recorder),
@@ -52,7 +52,7 @@ const collectVariableSchema = (document, recorder) => {
52
52
  if (attribute.value.kind !== "string") continue;
53
53
  if (node.tag === "If" && attribute.name === "test") {
54
54
  const parsed = parsePredicate(attribute.value.value, 0);
55
- if (Result.isSuccess(parsed)) for (const name of collectPredicateVariables(parsed.success)) (insideIf ? conditional : unconditional).add(name);
55
+ if (Result.isOk(parsed)) for (const name of collectPredicateVariables(parsed.value)) (insideIf ? conditional : unconditional).add(name);
56
56
  continue;
57
57
  }
58
58
  for (const match of attribute.value.value.matchAll(INTERPOLATION_PATTERN)) {
@@ -1,4 +1,4 @@
1
- import { r as PresetCategory } from "./catalog-Ck1tAGwO.js";
1
+ import { r as PresetCategory } from "./catalog-60eaYC7b.js";
2
2
  //#region presets/showcase.d.ts
3
3
  /**
4
4
  * The showcase projects: presets authored as complete editable projects under
@@ -453,4 +453,4 @@ declare const showcaseSourcePath: (project: ShowcaseProject) => string;
453
453
  declare const showcaseProjectPaths: (project: ShowcaseProject) => ReadonlyArray<string>;
454
454
  //#endregion
455
455
  export { ShowcaseProjectId as a, showcaseProject as c, showcaseSourcePath as d, ShowcaseProject as i, showcaseProjectPaths as l, OwnedShowcaseId as n, ShowcaseProvenance as o, SHOWCASE_ROOT as r, isShowcaseProjectId as s, BrandRecreationShowcaseId as t, showcaseProjects as u };
456
- //# sourceMappingURL=showcase-C_xgl6hz.d.ts.map
456
+ //# sourceMappingURL=showcase-DGZAWn4Z.d.ts.map
@@ -1,5 +1,4 @@
1
- import { E as SmlNode, m as ElementNode } from "./ast-63I-VLQx.js";
2
- import { Schema } from "effect";
1
+ import { f as ElementNode, x as SmlNode } from "./ast-BrDtApwS.js";
3
2
  //#region src/source-locations.d.ts
4
3
  /**
5
4
  * The source payload emitted by the JSX development transform. The transform
@@ -17,14 +16,6 @@ interface JsxSourceLocation {
17
16
  readonly lineNumber: number;
18
17
  readonly columnNumber: number;
19
18
  }
20
- /** JSON-safe source location shape shared by preview and editor contracts. */
21
- declare const JsxSourceLocation: Schema.Struct<{
22
- readonly fileName: Schema.String;
23
- readonly lineNumber: Schema.Int;
24
- readonly columnNumber: Schema.Int;
25
- }>;
26
- /** Reasons a preview cannot safely expose an authored source map. */
27
- declare const SourceLocationUnavailableReason: Schema.Literals<readonly ["no-source-metadata", "structure-mismatch", "duplicate-source-origin", "render-failed", "stale-preview"]>;
28
19
  /**
29
20
  * Runtime-only carrier for JSX source metadata. It is global so separately
30
21
  * bundled `jsx-dev-runtime` and `render-template` entries still agree on the
@@ -38,7 +29,7 @@ declare const JSX_SOURCE_LOCATION_PROP: "__samva_source_location";
38
29
  * currently provide no source metadata, so that path reports a typed absence
39
30
  * rather than guessing from output order.
40
31
  */
41
- type SourceLocationUnavailableReason = typeof SourceLocationUnavailableReason.Type;
32
+ type SourceLocationUnavailableReason = "no-source-metadata" | "structure-mismatch" | "duplicate-source-origin" | "render-failed" | "stale-preview";
42
33
  type UnboundTemplateSourceLocations = {
43
34
  readonly kind: "available";
44
35
  /** Parsed SML node id → the JSX origin that produced that element. */
@@ -57,19 +48,6 @@ type TemplateSourceLocations = {
57
48
  readonly kind: "unavailable";
58
49
  readonly reason: SourceLocationUnavailableReason;
59
50
  };
60
- /** JSON-safe schema for a parsed-node-id → JSX-origin map. */
61
- declare const TemplateSourceLocationsSchema: Schema.Union<readonly [Schema.Struct<{
62
- readonly kind: Schema.Literal<"available">;
63
- readonly revision: Schema.String;
64
- readonly byNodeId: Schema.$Record<Schema.String, Schema.Struct<{
65
- readonly fileName: Schema.String;
66
- readonly lineNumber: Schema.Int;
67
- readonly columnNumber: Schema.Int;
68
- }>>;
69
- }>, Schema.Struct<{
70
- readonly kind: Schema.Literal<"unavailable">;
71
- readonly reason: Schema.Literals<readonly ["no-source-metadata", "structure-mismatch", "duplicate-source-origin", "render-failed", "stale-preview"]>;
72
- }>]>;
73
51
  /** Normalize the optional payload accepted by `jsxDEV`. */
74
52
  declare const completeJsxSourceLocation: (source: JsxSource | null | undefined) => JsxSourceLocation | undefined;
75
53
  /** Attach a source origin without changing the serialized draft shape. */
@@ -85,5 +63,5 @@ declare const mapJsxSourceLocations: (draft: ElementNode, parsed: ElementNode) =
85
63
  /** Compare source-map statuses for the emission determinism guard. */
86
64
  declare const sameTemplateSourceLocations: (left: UnboundTemplateSourceLocations, right: UnboundTemplateSourceLocations) => boolean;
87
65
  //#endregion
88
- export { SourceLocationUnavailableReason as a, UnboundTemplateSourceLocations as c, jsxSourceLocationOf as d, mapJsxSourceLocations as f, JsxSourceLocation as i, attachJsxSourceLocation as l, JSX_SOURCE_LOCATION_PROP as n, TemplateSourceLocations as o, sameTemplateSourceLocations as p, JsxSource as r, TemplateSourceLocationsSchema as s, JSX_SOURCE_LOCATION as t, completeJsxSourceLocation as u };
89
- //# sourceMappingURL=source-locations-BCJQnIfU.d.ts.map
66
+ export { SourceLocationUnavailableReason as a, attachJsxSourceLocation as c, mapJsxSourceLocations as d, sameTemplateSourceLocations as f, JsxSourceLocation as i, completeJsxSourceLocation as l, JSX_SOURCE_LOCATION_PROP as n, TemplateSourceLocations as o, JsxSource as r, UnboundTemplateSourceLocations as s, JSX_SOURCE_LOCATION as t, jsxSourceLocationOf as u };
67
+ //# sourceMappingURL=source-locations-DrCM82Ym.d.ts.map
@@ -1,2 +1,2 @@
1
- import { a as SourceLocationUnavailableReason, c as UnboundTemplateSourceLocations, d as jsxSourceLocationOf, f as mapJsxSourceLocations, i as JsxSourceLocation, l as attachJsxSourceLocation, n as JSX_SOURCE_LOCATION_PROP, o as TemplateSourceLocations, p as sameTemplateSourceLocations, r as JsxSource, s as TemplateSourceLocationsSchema, t as JSX_SOURCE_LOCATION, u as completeJsxSourceLocation } from "./source-locations-BCJQnIfU.js";
2
- export { JSX_SOURCE_LOCATION, JSX_SOURCE_LOCATION_PROP, JsxSource, JsxSourceLocation, SourceLocationUnavailableReason, TemplateSourceLocations, TemplateSourceLocationsSchema, UnboundTemplateSourceLocations, attachJsxSourceLocation, completeJsxSourceLocation, jsxSourceLocationOf, mapJsxSourceLocations, sameTemplateSourceLocations };
1
+ import { a as SourceLocationUnavailableReason, c as attachJsxSourceLocation, d as mapJsxSourceLocations, f as sameTemplateSourceLocations, i as JsxSourceLocation, l as completeJsxSourceLocation, n as JSX_SOURCE_LOCATION_PROP, o as TemplateSourceLocations, r as JsxSource, s as UnboundTemplateSourceLocations, t as JSX_SOURCE_LOCATION, u as jsxSourceLocationOf } from "./source-locations-DrCM82Ym.js";
2
+ export { JSX_SOURCE_LOCATION, JSX_SOURCE_LOCATION_PROP, JsxSource, JsxSourceLocation, SourceLocationUnavailableReason, TemplateSourceLocations, UnboundTemplateSourceLocations, attachJsxSourceLocation, completeJsxSourceLocation, jsxSourceLocationOf, mapJsxSourceLocations, sameTemplateSourceLocations };
@@ -1,19 +1,5 @@
1
- import { Predicate, Schema } from "effect";
1
+ import { t as hasProperty } from "./guards-eV1qLbf2.js";
2
2
  //#region src/source-locations.ts
3
- /** JSON-safe source location shape shared by preview and editor contracts. */
4
- const JsxSourceLocation = Schema.Struct({
5
- fileName: Schema.String,
6
- lineNumber: Schema.Int.check(Schema.isGreaterThanOrEqualTo(1)),
7
- columnNumber: Schema.Int.check(Schema.isGreaterThanOrEqualTo(1))
8
- });
9
- /** Reasons a preview cannot safely expose an authored source map. */
10
- const SourceLocationUnavailableReason = Schema.Literals([
11
- "no-source-metadata",
12
- "structure-mismatch",
13
- "duplicate-source-origin",
14
- "render-failed",
15
- "stale-preview"
16
- ]);
17
3
  /**
18
4
  * Runtime-only carrier for JSX source metadata. It is global so separately
19
5
  * bundled `jsx-dev-runtime` and `render-template` entries still agree on the
@@ -22,15 +8,6 @@ const SourceLocationUnavailableReason = Schema.Literals([
22
8
  const JSX_SOURCE_LOCATION = Symbol.for("@samva/markup/jsx-source-location");
23
9
  /** Internal JSX prop used only by an editor preview source transform. */
24
10
  const JSX_SOURCE_LOCATION_PROP = "__samva_source_location";
25
- /** JSON-safe schema for a parsed-node-id → JSX-origin map. */
26
- const TemplateSourceLocationsSchema = Schema.Union([Schema.Struct({
27
- kind: Schema.Literal("available"),
28
- revision: Schema.String,
29
- byNodeId: Schema.Record(Schema.String, JsxSourceLocation)
30
- }), Schema.Struct({
31
- kind: Schema.Literal("unavailable"),
32
- reason: SourceLocationUnavailableReason
33
- })]);
34
11
  /** Normalize the optional payload accepted by `jsxDEV`. */
35
12
  const completeJsxSourceLocation = (source) => {
36
13
  if (source?.fileName === void 0 || source.lineNumber === void 0 || source.columnNumber === void 0) return;
@@ -50,7 +27,7 @@ const attachJsxSourceLocation = (node, source) => {
50
27
  writable: false
51
28
  });
52
29
  };
53
- const isJsxSourceLocation = (value) => Predicate.hasProperty(value, "fileName") && typeof value.fileName === "string" && Predicate.hasProperty(value, "lineNumber") && typeof value.lineNumber === "number" && Number.isInteger(value.lineNumber) && Predicate.hasProperty(value, "columnNumber") && typeof value.columnNumber === "number" && Number.isInteger(value.columnNumber);
30
+ const isJsxSourceLocation = (value) => hasProperty(value, "fileName") && typeof value.fileName === "string" && hasProperty(value, "lineNumber") && typeof value.lineNumber === "number" && Number.isInteger(value.lineNumber) && hasProperty(value, "columnNumber") && typeof value.columnNumber === "number" && Number.isInteger(value.columnNumber);
54
31
  /** Read the ephemeral origin, if this draft element has one. */
55
32
  const jsxSourceLocationOf = (node) => {
56
33
  if (node.kind !== "element") return void 0;
@@ -120,6 +97,6 @@ const sameTemplateSourceLocations = (left, right) => {
120
97
  return true;
121
98
  };
122
99
  //#endregion
123
- export { JSX_SOURCE_LOCATION, JSX_SOURCE_LOCATION_PROP, JsxSourceLocation, SourceLocationUnavailableReason, TemplateSourceLocationsSchema, attachJsxSourceLocation, completeJsxSourceLocation, jsxSourceLocationOf, mapJsxSourceLocations, sameTemplateSourceLocations };
100
+ export { JSX_SOURCE_LOCATION, JSX_SOURCE_LOCATION_PROP, attachJsxSourceLocation, completeJsxSourceLocation, jsxSourceLocationOf, mapJsxSourceLocations, sameTemplateSourceLocations };
124
101
 
125
102
  //# sourceMappingURL=source-locations.js.map
@@ -1,6 +1,6 @@
1
- import { m as EmailNode } from "./elements-DtwKlFVH.js";
2
- import { r as InputSchema } from "./input-schema-BIfzI63B.js";
3
- import { StandardJSONSchemaV1 } from "effect/StandardSchema";
1
+ import { m as EmailNode } from "./elements-BzjjnoCS.js";
2
+ import { r as InputSchema } from "./input-schema-7dPFVvNE.js";
3
+ import { StandardJSONSchemaV1 } from "@standard-schema/spec";
4
4
  //#region src/template.d.ts
5
5
  /** One actual-input render owns all of a message's authored content. */
6
6
  interface EmailContent {
@@ -57,4 +57,4 @@ type TemplateInput<T extends {
57
57
  }> = Parameters<T["render"]>[0];
58
58
  //#endregion
59
59
  export { TemplateRuntime as a, TemplateInput as i, TEMPLATE_NAME_PATTERN as n, defineEmail as o, TemplateDefinition as r, isTemplateDefinition as s, EmailContent as t };
60
- //# sourceMappingURL=template-Dy5yOPyp.d.ts.map
60
+ //# sourceMappingURL=template-DWftqh4x.d.ts.map
@@ -1,2 +1,2 @@
1
- import { a as TemplateRuntime, i as TemplateInput, n as TEMPLATE_NAME_PATTERN, o as defineEmail, r as TemplateDefinition, s as isTemplateDefinition, t as EmailContent } from "./template-Dy5yOPyp.js";
1
+ import { a as TemplateRuntime, i as TemplateInput, n as TEMPLATE_NAME_PATTERN, o as defineEmail, r as TemplateDefinition, s as isTemplateDefinition, t as EmailContent } from "./template-DWftqh4x.js";
2
2
  export { EmailContent, TEMPLATE_NAME_PATTERN, TemplateDefinition, TemplateInput, TemplateRuntime, defineEmail, isTemplateDefinition };
package/dist/template.js CHANGED
@@ -1,8 +1,8 @@
1
- import { inputSchema } from "./input-schema.js";
2
- import { Predicate } from "effect";
1
+ import { n as isObject } from "./guards-eV1qLbf2.js";
2
+ import { n as inputSchema } from "./input-schema-zjkgrXn2.js";
3
3
  //#region src/template.ts
4
4
  /** Classify an evaluated module export without executing helpers as templates. */
5
- const isTemplateDefinition = (value) => Predicate.isObject(value) && typeof value.id === "string" && typeof value.render === "function" && Predicate.isObject(value.schema);
5
+ const isTemplateDefinition = (value) => isObject(value) && typeof value.id === "string" && typeof value.render === "function" && isObject(value.schema);
6
6
  /**
7
7
  * The one naming contract for a template id and a fixture key: a letter, a digit or `_` first,
8
8
  * then those plus `.`, `_` and `-`.
@@ -1,6 +1,6 @@
1
- import { t as Diagnostic } from "./diagnostics-oveE-laV.js";
1
+ import { t as Diagnostic } from "./diagnostics-OcQKQYSY.js";
2
2
  import { o as TokenOverrides } from "./theme-6ApjjiBo.js";
3
- import { Result } from "effect";
3
+ import { Result } from "better-result";
4
4
  //#region src/theme-css.d.ts
5
5
  /**
6
6
  * `TokenOverrides` plus `tracking`. The compiler's `TokenOverrides` names its
@@ -12,7 +12,7 @@ interface ParsedTheme extends TokenOverrides {
12
12
  /** tracking-{name} → letter-spacing in em (negative allowed). */
13
13
  readonly tracking?: Readonly<Record<string, number>> | undefined;
14
14
  }
15
- declare const parseThemeCss: (css: string) => Result.Result<ParsedTheme, ReadonlyArray<Diagnostic>>;
15
+ declare const parseThemeCss: (css: string) => Result<ParsedTheme, ReadonlyArray<Diagnostic>>;
16
16
  /**
17
17
  * Serialize a ParsedTheme to `@theme` CSS. Light tokens go in the top-level
18
18
  * `@theme` block; `colorsDark` (colors-only, matching the parser) goes in the
package/dist/theme-css.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Result } from "effect";
1
+ import { Result } from "better-result";
2
2
  //#region src/theme-css.ts
3
3
  const CHAR_TAB = 9;
4
4
  const CHAR_LINE_FEED = 10;
@@ -441,7 +441,7 @@ const parseThemeCss = (css) => {
441
441
  start: 0,
442
442
  end: Math.min(1, length)
443
443
  });
444
- if (diagnostics.length > 0) return Result.fail(diagnostics);
444
+ if (diagnostics.length > 0) return Result.err(diagnostics);
445
445
  const nonEmpty = (entries) => Object.keys(entries).length > 0 ? entries : void 0;
446
446
  const colors = nonEmpty(accumulator.colors);
447
447
  const colorsDark = nonEmpty(accumulator.colorsDark);
@@ -452,7 +452,7 @@ const parseThemeCss = (css) => {
452
452
  const fontFamilies = nonEmpty(accumulator.fontFamilies);
453
453
  const containerPx = nonEmpty(accumulator.containerPx);
454
454
  const tracking = nonEmpty(accumulator.tracking);
455
- return Result.succeed({
455
+ return Result.ok({
456
456
  colors,
457
457
  colorsDark,
458
458
  spacingPx: accumulator.spacingPx,
package/dist/theme.js CHANGED
@@ -16,7 +16,30 @@
16
16
  * quoted CSS string `\/` is identical to `/`, but the HTML tokenizer no longer
17
17
  * matches `<\/style>` as a raw-text end tag.
18
18
  */
19
- const escapeCssString = (value) => value.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/<\//g, "<\\/");
19
+ const escapeCssString = (value) => value.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/[\u0000-\u001f\u007f]/g, (character) => `\\${character.charCodeAt(0).toString(16)} `).replace(/<\//g, "<\\/");
20
+ const FONT_FAMILY_NAME = /^-?[a-zA-Z][a-zA-Z0-9 -]*$/;
21
+ const SINGLE_QUOTED_FONT_FAMILY = /^'[^'\\<\u0000-\u001f\u007f]*'$/;
22
+ const DOUBLE_QUOTED_FONT_FAMILY = /^"[^"\\<\u0000-\u001f\u007f]*"$/;
23
+ const splitFontStack = (value) => {
24
+ const families = [];
25
+ let start = 0;
26
+ let quote;
27
+ for (let index = 0; index < value.length; index++) {
28
+ const character = value[index];
29
+ if (quote !== void 0) {
30
+ if (character === quote) quote = void 0;
31
+ } else if (character === "'" || character === "\"") quote = character;
32
+ else if (character === ",") {
33
+ families.push(value.slice(start, index).trim());
34
+ start = index + 1;
35
+ }
36
+ }
37
+ families.push(value.slice(start).trim());
38
+ return families;
39
+ };
40
+ const safeFontStack = (value) => {
41
+ return splitFontStack(value).every((family) => FONT_FAMILY_NAME.test(family) || SINGLE_QUOTED_FONT_FAMILY.test(family) || DOUBLE_QUOTED_FONT_FAMILY.test(family)) ? value : `'${escapeCssString(value)}'`;
42
+ };
20
43
  /**
21
44
  * Flatten persisted font tokens into the compiler's two font fields, so the
22
45
  * server (apps/api) and the in-browser canvas (apps/web) produce byte-identical
@@ -31,7 +54,7 @@ const flattenFontTokens = (tokens) => {
31
54
  const fontFamilies = {};
32
55
  const fontFaces = {};
33
56
  for (const [name, token] of Object.entries(tokens)) {
34
- fontFamilies[name] = `'${escapeCssString(token.family)}', ${token.fallback}`;
57
+ fontFamilies[name] = `'${escapeCssString(token.family)}', ${safeFontStack(token.fallback)}`;
35
58
  if (token.faces != null) fontFaces[name] = {
36
59
  family: token.family,
37
60
  faces: token.faces
@@ -1,7 +1,5 @@
1
1
  //#region src/tsx-source-instrumentation.d.ts
2
- declare const TsxSourceInstrumentationError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
3
- readonly _tag: "TsxSourceInstrumentationError";
4
- } & Readonly<A>;
2
+ declare const TsxSourceInstrumentationError_base: import("better-result").TaggedErrorClass<"TsxSourceInstrumentationError">;
5
3
  declare class TsxSourceInstrumentationError extends TsxSourceInstrumentationError_base<{
6
4
  readonly message: string;
7
5
  readonly cause?: unknown;
@@ -1,17 +1,17 @@
1
+ import { n as isObject } from "./guards-eV1qLbf2.js";
1
2
  import { JSX_SOURCE_LOCATION_PROP } from "./source-locations.js";
2
3
  import { buildLineMap, lineColumn } from "./diagnostics.js";
3
- import { Data, Predicate } from "effect";
4
+ import { TaggedError } from "better-result";
4
5
  import { parse } from "@babel/parser";
5
6
  //#region src/tsx-source-instrumentation.ts
6
- var TsxSourceInstrumentationError = class extends Data.TaggedError("TsxSourceInstrumentationError") {};
7
- const isRecord = (value) => Predicate.isObject(value) && !Array.isArray(value);
8
- const isAstNode = (value) => isRecord(value) && typeof value.type === "string" && typeof value.start === "number" && typeof value.end === "number";
7
+ var TsxSourceInstrumentationError = class extends TaggedError("TsxSourceInstrumentationError") {};
8
+ const isAstNode = (value) => isObject(value) && typeof value.type === "string" && typeof value.start === "number" && typeof value.end === "number";
9
9
  const walkAst = (value, visit) => {
10
10
  if (Array.isArray(value)) {
11
11
  for (const item of value) walkAst(item, visit);
12
12
  return;
13
13
  }
14
- if (!isRecord(value)) return;
14
+ if (!isObject(value)) return;
15
15
  if (isAstNode(value)) visit(value);
16
16
  for (const [key, child] of Object.entries(value)) {
17
17
  if (key === "loc" || key === "extra" || key === "comments" || key === "tokens") continue;
@@ -22,7 +22,7 @@ const jsxIdentifier = (value) => {
22
22
  if (!isAstNode(value) || value.type !== "JSXIdentifier") return void 0;
23
23
  return typeof value.name === "string" ? value.name : void 0;
24
24
  };
25
- const parseErrorMessage = (error) => isRecord(error) && typeof error.message === "string" ? error.message : "The authored TSX could not be parsed for preview instrumentation.";
25
+ const parseErrorMessage = (error) => isObject(error) && typeof error.message === "string" ? error.message : "The authored TSX could not be parsed for preview instrumentation.";
26
26
  const marker = (fileName, source, offset, token) => {
27
27
  const position = lineColumn(buildLineMap(source), offset);
28
28
  return ` ${JSX_SOURCE_LOCATION_PROP}={${JSON.stringify({