@samva/markup 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/AGENTS.md +25 -5
  2. package/README.md +92 -26
  3. package/dist/{ast-DAhnAvQN.d.ts → ast-63I-VLQx.d.ts} +15 -2
  4. package/dist/ast.d.ts +2 -2
  5. package/dist/ast.js +14 -1
  6. package/dist/{checks-D-zFQt5R.js → checks-DXuERO4B.js} +36 -2
  7. package/dist/checks.d.ts +8 -1
  8. package/dist/checks.js +2 -2
  9. package/dist/compile-sms.d.ts +2 -2
  10. package/dist/compile-whatsapp.d.ts +2 -2
  11. package/dist/compile.d.ts +9 -84
  12. package/dist/compile.js +11 -920
  13. package/dist/compiler.d.ts +301 -0
  14. package/dist/compiler.js +1389 -0
  15. package/dist/components.d.ts +3 -3
  16. package/dist/corpus-Dz5irJ9x.d.ts +161 -0
  17. package/dist/diagnostics-Djd9ckLg.d.ts +90 -0
  18. package/dist/diagnostics.js +4 -1
  19. package/dist/elements-DtwKlFVH.d.ts +820 -0
  20. package/dist/email/components.d.ts +119 -0
  21. package/dist/email/components.js +207 -0
  22. package/dist/email/diagnostics.d.ts +2 -0
  23. package/dist/email/diagnostics.js +56 -0
  24. package/dist/email/elements.d.ts +2 -0
  25. package/dist/email/elements.js +299 -0
  26. package/dist/email/html.d.ts +42 -0
  27. package/dist/email/html.js +266 -0
  28. package/dist/email/jsx-dev-runtime.d.ts +26 -0
  29. package/dist/email/jsx-dev-runtime.js +12 -0
  30. package/dist/email/jsx-runtime.d.ts +2 -0
  31. package/dist/email/jsx-runtime.js +60 -0
  32. package/dist/email/render.d.ts +36 -0
  33. package/dist/email/render.js +6 -0
  34. package/dist/email/selection.d.ts +102 -0
  35. package/dist/email/selection.js +143 -0
  36. package/dist/email/source-edit.d.ts +67 -0
  37. package/dist/email/source-edit.js +185 -0
  38. package/dist/email/stylesheets.d.ts +2 -0
  39. package/dist/email/stylesheets.js +43 -0
  40. package/dist/email/text.d.ts +7 -0
  41. package/dist/email/text.js +92 -0
  42. package/dist/emit.d.ts +13 -12
  43. package/dist/emit.js +63 -63
  44. package/dist/input-schema-BIfzI63B.d.ts +23 -0
  45. package/dist/input-schema.d.ts +2 -0
  46. package/dist/input-schema.js +170 -0
  47. package/dist/jsx-dev-runtime.d.ts +4 -3
  48. package/dist/jsx-dev-runtime.js +9 -1
  49. package/dist/{jsx-runtime-eNIMY87F.d.ts → jsx-runtime-C5k_Zq0A.d.ts} +3 -3
  50. package/dist/jsx-runtime.d.ts +1 -1
  51. package/dist/jsx-runtime.js +20 -1
  52. package/dist/model.d.ts +1 -1
  53. package/dist/ops.d.ts +1 -1
  54. package/dist/ops.js +1 -1
  55. package/dist/{parser-DkQKIjDa.d.ts → parser-CGBS73Gz.d.ts} +2 -2
  56. package/dist/parser.d.ts +1 -1
  57. package/dist/presets/category-mapping.d.ts +1 -1
  58. package/dist/presets/corpus.d.ts +2 -0
  59. package/dist/presets/corpus.js +186 -0
  60. package/dist/presets/public-gallery.d.ts +272 -2
  61. package/dist/presets/public-gallery.js +305 -6
  62. package/dist/presets/public-handoff.d.ts +140 -2
  63. package/dist/presets/public-handoff.js +130 -33
  64. package/dist/presets/public-preview-artifacts.d.ts +2 -2
  65. package/dist/presets/public-preview-artifacts.js +20 -26
  66. package/dist/presets/public-preview.d.ts +56 -2
  67. package/dist/presets/public-preview.js +388 -1152
  68. package/dist/presets/sources.d.ts +17 -4
  69. package/dist/presets/sources.js +1212 -43
  70. package/dist/presets.d.ts +2 -2
  71. package/dist/printer.d.ts +1 -1
  72. package/dist/render-Cddsy_XF.js +585 -0
  73. package/dist/render-template.d.ts +5 -2
  74. package/dist/render-template.js +3 -1
  75. package/dist/source-locations-BCJQnIfU.d.ts +89 -0
  76. package/dist/source-locations.d.ts +2 -0
  77. package/dist/source-locations.js +125 -0
  78. package/dist/stylesheets-oDRAnYA3.d.ts +94 -0
  79. package/dist/template-Dy5yOPyp.d.ts +60 -0
  80. package/dist/template.d.ts +2 -0
  81. package/dist/template.js +37 -0
  82. package/dist/tsx-source-instrumentation.d.ts +23 -0
  83. package/dist/tsx-source-instrumentation.js +97 -0
  84. package/dist/tsx-tracer.d.ts +116 -0
  85. package/dist/tsx-tracer.js +985 -0
  86. package/generated/sml-agent-contract.json +574 -782
  87. package/generated/sml-email-agent-contract.json +502 -677
  88. package/generated/sml-email-language-reference.md +94 -160
  89. package/generated/sml-language-reference.md +195 -220
  90. package/package.json +111 -9
  91. package/dist/public-gallery-D0Zv0pZE.d.ts +0 -611
  92. package/dist/public-preview-u1Z9aaGq.d.ts +0 -1168
@@ -1,26 +1,120 @@
1
- import { collectVariableReferences } from "../ast.js";
2
- import { parseSml } from "../parser.js";
3
- import { resolveTokenTable } from "../theme.js";
4
- import { baseTokens } from "../tokens.js";
5
- import { compileSource } from "../compile.js";
6
- import { RECOMMENDED_THEME, presetCatalog } from "../presets.js";
1
+ import { EMAIL_ELEMENTS } from "../email/elements.js";
2
+ import { presetCatalog } from "../presets.js";
7
3
  import { publicTemplates, templateCollections } from "./public-gallery.js";
8
- import { Result } from "effect";
4
+ import { Predicate, Result } from "effect";
5
+ import { parse } from "@babel/parser";
9
6
  //#region presets/public-handoff.ts
10
7
  const SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
11
8
  const ISO_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;
12
9
  const MAX_HTTPS_ASSET_LENGTH = 2048;
13
10
  const MAX_DATA_IMAGE_ASSET_LENGTH = 32768;
14
11
  const DATA_IMAGE = /^data:image\/(?:avif|gif|jpeg|png|svg\+xml|webp)(?:;charset=[a-z0-9-]+)?(?:;base64)?,[^\r\n]+$/i;
15
- const isTemplateReferenceProvenanceSource = (value) => value === "sanitized-synthetic" || value === "reviewed-public";
16
- const PUBLIC_TEMPLATE_FONT_TOKENS = new Set(Object.keys(resolveTokenTable({
17
- base: baseTokens,
18
- overrides: [RECOMMENDED_THEME]
19
- }).fontFamilies));
20
- const FONT_WEIGHT_TOKENS = new Set(Object.keys(resolveTokenTable({
21
- base: baseTokens,
22
- overrides: [RECOMMENDED_THEME]
23
- }).fontWeights));
12
+ const PUBLIC_TEMPLATE_ASSET_PATH = /^\/templates\/assets\/[A-Za-z0-9][A-Za-z0-9._-]*\.(?:jpe?g|png|webp)$/;
13
+ const BLOCKED_PUBLIC_ASSET = /(?:unsplash|placehold|assets\.example)/i;
14
+ const isTemplateReferenceProvenanceSource = (value) => value === "samva-owned" || value === "sanitized-synthetic" || value === "reviewed-public";
15
+ /** Bound authored source before parsing or mounting it. */
16
+ const PUBLIC_TEMPLATE_SOURCE_MAX_CHARACTERS = 512e3;
17
+ const PUBLIC_TEMPLATE_FONT_TOKENS = /* @__PURE__ */ new Set([
18
+ "sans",
19
+ "serif",
20
+ "mono"
21
+ ]);
22
+ const FONT_WEIGHT_TOKENS = /* @__PURE__ */ new Set([
23
+ "thin",
24
+ "extralight",
25
+ "light",
26
+ "normal",
27
+ "medium",
28
+ "semibold",
29
+ "bold",
30
+ "extrabold",
31
+ "black"
32
+ ]);
33
+ const parsePublicTemplateSource = (source) => {
34
+ if (source.length === 0 || source.length > 512e3) return void 0;
35
+ try {
36
+ const parsed = parse(source, {
37
+ sourceType: "module",
38
+ plugins: ["typescript", "jsx"]
39
+ });
40
+ return parsed.program.body.some((node) => node.type === "ExportDefaultDeclaration") ? parsed : void 0;
41
+ } catch {
42
+ return;
43
+ }
44
+ };
45
+ const PUBLIC_COMPONENT_TAGS = /* @__PURE__ */ new Set([
46
+ ...Object.keys(EMAIL_ELEMENTS),
47
+ "Email",
48
+ "Section",
49
+ "Columns",
50
+ "Column",
51
+ "Button",
52
+ "Spacer",
53
+ "Divider",
54
+ "Image",
55
+ "Link"
56
+ ]);
57
+ /** Only generic element names and literal class colors survive deidentification. */
58
+ const publicTemplateAuthoredStructure = (source) => {
59
+ const parsed = parsePublicTemplateSource(source);
60
+ if (parsed === void 0) return void 0;
61
+ const nodes = [];
62
+ const counts = /* @__PURE__ */ new Map();
63
+ const palette = /* @__PURE__ */ new Set();
64
+ const siblings = /* @__PURE__ */ new Map();
65
+ const walk = (value, depth) => {
66
+ if (Array.isArray(value)) {
67
+ for (const item of value) walk(item, depth);
68
+ return;
69
+ }
70
+ if (!Predicate.isObject(value)) return;
71
+ let childDepth = depth;
72
+ if (value.type === "JSXElement") {
73
+ const opening = value.openingElement;
74
+ if (!Predicate.isObject(opening)) return;
75
+ const name = opening.name;
76
+ const tag = Predicate.isObject(name) && typeof name.name === "string" && PUBLIC_COMPONENT_TAGS.has(name.name) ? name.name : "Component";
77
+ const childIndex = siblings.get(depth) ?? 0;
78
+ siblings.set(depth, childIndex + 1);
79
+ siblings.set(depth + 1, 0);
80
+ if (nodes.length < 200) nodes.push({
81
+ tag,
82
+ depth,
83
+ childIndex
84
+ });
85
+ counts.set(tag, (counts.get(tag) ?? 0) + 1);
86
+ if (Array.isArray(opening.attributes)) for (const attribute of opening.attributes) {
87
+ if (!Predicate.isObject(attribute) || !Predicate.isObject(attribute.name) || attribute.name.name !== "className") continue;
88
+ const literal = attribute.value;
89
+ if (!Predicate.isObject(literal) || literal.type !== "StringLiteral" || typeof literal.value !== "string") continue;
90
+ for (const match of literal.value.matchAll(/#([a-f0-9]{8}|[a-f0-9]{6}|[a-f0-9]{3})(?=[^a-f0-9]|$)/gi)) if (palette.size < 24) palette.add(`#${match[1].toLowerCase()}`);
91
+ }
92
+ childDepth += 1;
93
+ }
94
+ for (const [key, child] of Object.entries(value)) {
95
+ if ([
96
+ "loc",
97
+ "comments",
98
+ "leadingComments",
99
+ "trailingComments",
100
+ "innerComments",
101
+ "extra",
102
+ "tokens"
103
+ ].includes(key)) continue;
104
+ walk(child, childDepth);
105
+ }
106
+ };
107
+ walk(parsed.program, 0);
108
+ return {
109
+ basis: "authored-jsx",
110
+ nodes,
111
+ components: [...counts].sort(([a], [b]) => a.localeCompare(b)).slice(0, 64).map(([tag, count]) => ({
112
+ tag,
113
+ count
114
+ })),
115
+ palette: [...palette].sort()
116
+ };
117
+ };
24
118
  const sortedUnique = (values) => [...new Set(values)].sort();
25
119
  const sameStrings = (left, right) => JSON.stringify(sortedUnique(left)) === JSON.stringify(sortedUnique(right));
26
120
  const nonEmpty = (value) => value.trim().length > 0;
@@ -30,22 +124,28 @@ const issue = (artifactId, code, message) => ({
30
124
  message
31
125
  });
32
126
  /**
33
- * Public imports may only refer to existing font-family tokens. A source may
127
+ * Public imports may only refer to the default email Tailwind font-family tokens. A source may
34
128
  * still use the supported `font-*` weight utilities; this check deliberately
35
129
  * rejects a new family token instead of creating a corpus font-hosting path.
36
130
  */
37
131
  const unsupportedPublicTemplateFontTokens = (source) => {
38
132
  const unsupported = /* @__PURE__ */ new Set();
39
- for (const match of source.matchAll(/\bclass\s*=\s*(["'])(.*?)\1/gs)) for (const className of (match[2] ?? "").split(/\s+/)) {
133
+ for (const match of source.matchAll(/\bclassName\s*=\s*(["'])(.*?)\1/gs)) for (const className of (match[2] ?? "").split(/\s+/)) {
40
134
  if (!className.startsWith("font-")) continue;
41
135
  const token = className.slice(5);
42
136
  if (!PUBLIC_TEMPLATE_FONT_TOKENS.has(token) && !FONT_WEIGHT_TOKENS.has(token)) unsupported.add(token);
43
137
  }
44
138
  return [...unsupported].sort();
45
139
  };
46
- /** Accept only bounded HTTPS URLs or the preview's self-contained image data. */
140
+ /**
141
+ * Accept bounded HTTPS URLs, same-origin gallery files under `/templates/assets/`,
142
+ * or the preview's self-contained image data. Live Unsplash, placehold, and
143
+ * assets.example hosts are never safe for public sample data.
144
+ */
47
145
  const isSafePublicTemplateAsset = (value) => {
146
+ if (value.includes("..") || BLOCKED_PUBLIC_ASSET.test(value)) return false;
48
147
  if (value.startsWith("data:")) return value.length <= MAX_DATA_IMAGE_ASSET_LENGTH && DATA_IMAGE.test(value);
148
+ if (PUBLIC_TEMPLATE_ASSET_PATH.test(value)) return value.length <= MAX_HTTPS_ASSET_LENGTH;
49
149
  const url = URL.parse(value);
50
150
  return value.length <= MAX_HTTPS_ASSET_LENGTH && url?.protocol === "https:" && url.hostname.length > 0 && url.username.length === 0 && url.password.length === 0;
51
151
  };
@@ -59,7 +159,7 @@ const toTemplateReferencePacket = (artifact) => ({
59
159
  },
60
160
  preview: artifact.preview,
61
161
  publicMetadata: artifact.publicMetadata,
62
- ...artifact.provenance === void 0 ? {} : { provenance: artifact.provenance }
162
+ provenance: artifact.provenance
63
163
  });
64
164
  const jsonProjection = (kind, value) => ({
65
165
  kind,
@@ -73,7 +173,7 @@ const jsonProjection = (kind, value) => ({
73
173
  const emitTemplateReferencePacket = (packet) => [
74
174
  {
75
175
  kind: "source",
76
- relativePath: "preset.samva",
176
+ relativePath: "preset.tsx",
77
177
  contents: packet.preset.source
78
178
  },
79
179
  jsonProjection("catalog", {
@@ -87,7 +187,7 @@ const emitTemplateReferencePacket = (packet) => [
87
187
  jsonProjection("gallery", {
88
188
  presetId: packet.preset.id,
89
189
  ...packet.gallery,
90
- ...packet.provenance === void 0 ? {} : { provenance: packet.provenance }
190
+ provenance: packet.provenance
91
191
  }),
92
192
  jsonProjection("preview", packet.preview),
93
193
  jsonProjection("editorial", {
@@ -99,6 +199,8 @@ const emitTemplateReferencePacket = (packet) => [
99
199
  })
100
200
  ];
101
201
  /**
202
+ * Validates metadata and TSX syntax, never executing the template. The Vite importer
203
+ * verifies schema, fixtures and rendered content before writing projections.
102
204
  * Validates explicitly supplied, owner-approved artifacts without filesystem,
103
205
  * network, persistence, or discovery. Successful output is a projection for
104
206
  * the existing preset, public-gallery, preview, and editorial contracts; this
@@ -115,22 +217,17 @@ const validatePublicTemplateHandoff = (artifacts) => {
115
217
  for (const artifact of artifacts) {
116
218
  const id = artifact.preset.id;
117
219
  if (artifact.approval.state !== "approved" || !nonEmpty(artifact.approval.approvedBy) || !ISO_TIMESTAMP.test(artifact.approval.approvedAt)) issues.push(issue(id, "approval", "owner approval and an ISO-8601 approval timestamp are required"));
118
- if (artifact.provenance !== void 0 && (!isTemplateReferenceProvenanceSource(artifact.provenance.source) || !nonEmpty(artifact.provenance.referenceId) || !ISO_TIMESTAMP.test(artifact.provenance.recordedAt))) issues.push(issue(id, "provenance", "provenance requires a supported source, identifier, and ISO-8601 timestamp"));
220
+ if (artifact.provenance === void 0 || !isTemplateReferenceProvenanceSource(artifact.provenance.source) || !nonEmpty(artifact.provenance.referenceId) || !ISO_TIMESTAMP.test(artifact.provenance.recordedAt)) issues.push(issue(id, "provenance", "provenance requires a supported source, identifier, and ISO-8601 timestamp"));
119
221
  if (!SLUG.test(id) || !SLUG.test(artifact.gallery.slug)) issues.push(issue(id, "identity", "preset id and canonical slug must be lowercase kebab-case"));
120
222
  if (existingIds.has(id) || suppliedIds.has(id)) issues.push(issue(id, "duplicate-id", `preset id "${id}" is already present`));
121
223
  if (existingSlugs.has(artifact.gallery.slug) || suppliedSlugs.has(artifact.gallery.slug)) issues.push(issue(id, "duplicate-slug", `canonical slug "${artifact.gallery.slug}" is already present`));
122
224
  suppliedIds.add(id);
123
225
  suppliedSlugs.add(artifact.gallery.slug);
124
- const compiled = compileSource(artifact.preset.source, { theme: { overrides: [RECOMMENDED_THEME] } });
125
- if (Result.isFailure(compiled)) issues.push(issue(id, "source", compiled.failure.message));
126
- else if (artifact.preset.channel !== "email" || compiled.success.channel !== "email") issues.push(issue(id, "channel", "public template source must compile as email"));
226
+ if (parsePublicTemplateSource(artifact.preset.source) === void 0) issues.push(issue(id, "source", "source must be a bounded TSX module with a default export; execution is verified by the importer"));
227
+ if (artifact.preset.channel !== "email") issues.push(issue(id, "channel", "public template source must be an executable email"));
228
+ if (new Set(artifact.preset.variables).size !== artifact.preset.variables.length || artifact.preset.variables.some((value) => !nonEmpty(value))) issues.push(issue(id, "variables", "declared input fields must be non-empty and unique"));
127
229
  const unsupportedFontTokens = unsupportedPublicTemplateFontTokens(artifact.preset.source);
128
230
  if (unsupportedFontTokens.length > 0) issues.push(issue(id, "font-token", `unsupported public font token: ${unsupportedFontTokens.join(", ")}`));
129
- const parsed = parseSml(artifact.preset.source);
130
- if (Result.isSuccess(parsed)) {
131
- const sourceVariables = [...collectVariableReferences(parsed.success)];
132
- if (!sameStrings(artifact.preset.variables, sourceVariables)) issues.push(issue(id, "variables", "declared variables must exactly match source references"));
133
- }
134
231
  const sampleVariables = Object.keys(artifact.preview.sampleData);
135
232
  if (!sameStrings(sampleVariables, artifact.preset.variables)) issues.push(issue(id, "sample-data", "sample data must cover every declared variable exactly"));
136
233
  if (Object.values(artifact.preview.sampleData).some((value) => !nonEmpty(value))) issues.push(issue(id, "sample-data", "sample data values must be non-empty"));
@@ -139,7 +236,7 @@ const validatePublicTemplateHandoff = (artifacts) => {
139
236
  for (const asset of artifact.preview.assets) {
140
237
  const value = artifact.preview.sampleData[asset.variable];
141
238
  if (!artifact.preset.variables.includes(asset.variable)) issues.push(issue(id, "assets", `asset "${asset.variable}" is not a declared preset variable`));
142
- if (value === void 0 || !isSafePublicTemplateAsset(value)) issues.push(issue(id, "assets", `asset "${asset.variable}" must reference a bounded HTTPS or data:image sample URL`));
239
+ if (value === void 0 || !isSafePublicTemplateAsset(value)) issues.push(issue(id, "assets", `asset "${asset.variable}" must reference a bounded HTTPS, /templates/assets, or data:image sample URL`));
143
240
  }
144
241
  const metadata = artifact.publicMetadata;
145
242
  const metadataStrings = [
@@ -186,6 +283,6 @@ const validatePublicTemplateHandoff = (artifacts) => {
186
283
  }));
187
284
  };
188
285
  //#endregion
189
- export { emitTemplateReferencePacket, isSafePublicTemplateAsset, toTemplateReferencePacket, unsupportedPublicTemplateFontTokens, validatePublicTemplateHandoff };
286
+ export { PUBLIC_TEMPLATE_SOURCE_MAX_CHARACTERS, emitTemplateReferencePacket, isSafePublicTemplateAsset, publicTemplateAuthoredStructure, toTemplateReferencePacket, unsupportedPublicTemplateFontTokens, validatePublicTemplateHandoff };
190
287
 
191
288
  //# sourceMappingURL=public-handoff.js.map
@@ -1,6 +1,6 @@
1
1
  import { EmailPresetId } from "../presets.js";
2
- import { n as PreviewQuality } from "../public-preview-u1Z9aaGq.js";
3
- import { t as PublicTemplate } from "../public-gallery-D0Zv0pZE.js";
2
+ import { PublicTemplate } from "./public-gallery.js";
3
+ import { PreviewQuality } from "./public-preview.js";
4
4
  import { Result } from "effect";
5
5
  //#region presets/public-preview-artifacts.d.ts
6
6
  type PreviewSurface = "card" | "detail" | "og";
@@ -1,9 +1,8 @@
1
- import { interpolate } from "../interpolate.js";
2
- import { compileSource } from "../compile.js";
3
- import { RECOMMENDED_THEME, presetCatalog } from "../presets.js";
1
+ import { presetCatalog } from "../presets.js";
2
+ import { registerPublicCorpusEntry } from "./corpus.js";
4
3
  import { publicTemplates } from "./public-gallery.js";
4
+ import { presetEmailFixtures, presetModuleSource } from "./sources.js";
5
5
  import { publicTemplatePreviews } from "./public-preview.js";
6
- import { presetSource } from "./sources.js";
7
6
  import { Result } from "effect";
8
7
  //#region presets/public-preview-artifacts.ts
9
8
  const renderFrames = [
@@ -46,29 +45,24 @@ const renderInputs = (slug, html) => renderFrames.flatMap((frame) => ["light", "
46
45
  })));
47
46
  /** Build one static preview artifact without loading the editor runtime or performing I/O. */
48
47
  const buildPublicTemplatePreviewArtifact = (template) => {
49
- const source = presetSource(template.presetId);
50
- const preset = presetCatalog.find((entry) => entry.id === template.presetId);
51
- const preview = publicTemplatePreviews[template.presetId];
52
- if (source === void 0 || preset === void 0) return Result.fail({
53
- presetId: template.presetId,
54
- message: "canonical preset source is missing"
48
+ const registration = registerPublicCorpusEntry({
49
+ id: template.presetId,
50
+ corpus: template.corpus,
51
+ facets: template.facets
55
52
  });
56
- const compiled = compileSource(source, { theme: { overrides: [RECOMMENDED_THEME] } });
57
- if (Result.isFailure(compiled) || compiled.success.channel !== "email") return Result.fail({
53
+ if (Result.isFailure(registration)) return Result.fail({
58
54
  presetId: template.presetId,
59
- message: "canonical preset did not compile as email"
55
+ message: `public corpus registration failed: ${registration.failure.message}`
60
56
  });
61
- const variableSchema = preset.variables.map((name) => ({
62
- name,
63
- type: "string",
64
- required: true
65
- }));
66
- const rendered = interpolate(compiled.success.html, preview.sampleData, variableSchema);
67
- if (rendered.missing.size > 0) return Result.fail({
57
+ const source = presetModuleSource(template.presetId);
58
+ const fixture = presetEmailFixtures[template.presetId]?.default;
59
+ const preset = presetCatalog.find((entry) => entry.id === template.presetId);
60
+ const preview = publicTemplatePreviews[template.presetId];
61
+ if (source === void 0 || preset === void 0 || fixture === void 0) return Result.fail({
68
62
  presetId: template.presetId,
69
- message: `sample data is missing: ${[...rendered.missing].sort().join(", ")}`
63
+ message: "canonical preset source is missing"
70
64
  });
71
- const sampleData = preview.sampleData;
65
+ const sampleData = fixture.input;
72
66
  const assets = [];
73
67
  for (const asset of preview.assets) {
74
68
  const value = sampleData[asset.variable];
@@ -81,13 +75,13 @@ const buildPublicTemplatePreviewArtifact = (template) => {
81
75
  return Result.succeed({
82
76
  presetId: template.presetId,
83
77
  slug: template.slug,
84
- subject: preview.subject,
85
- preheader: preview.preheader,
78
+ subject: fixture.subject,
79
+ preheader: fixture.preheader,
86
80
  quality: preview.quality,
87
81
  source,
88
- html: rendered.rendered,
82
+ html: fixture.html,
89
83
  assets,
90
- renders: renderInputs(template.slug, rendered.rendered)
84
+ renders: renderInputs(template.slug, fixture.html)
91
85
  });
92
86
  };
93
87
  /** Build only the requested canonical subset, preserving manifest order and aggregated failures. */
@@ -1,2 +1,56 @@
1
- import { i as publicTemplatePreviews, n as PreviewQuality, r as PublicTemplatePreview, t as PUBLIC_PREVIEW_LOGO_DATA_URI } from "../public-preview-u1Z9aaGq.js";
2
- export { PUBLIC_PREVIEW_LOGO_DATA_URI, PreviewQuality, PublicTemplatePreview, publicTemplatePreviews };
1
+ //#region presets/public-preview.d.ts
2
+ type PreviewQuality = "ready" | "asset-free";
3
+ interface PublicTemplatePreview {
4
+ readonly subject: string;
5
+ readonly preheader: string;
6
+ readonly quality: PreviewQuality;
7
+ readonly sampleData: Readonly<Record<string, string>>;
8
+ readonly assets: ReadonlyArray<{
9
+ readonly variable: string;
10
+ readonly kind: "image" | "logo";
11
+ }>;
12
+ }
13
+ /** Gallery metadata augments the actual default fixture; it does not supply render input. */
14
+ declare const publicTemplatePreviews: {
15
+ readonly welcome: PublicTemplatePreview;
16
+ readonly "email-verification": PublicTemplatePreview;
17
+ readonly otp: PublicTemplatePreview;
18
+ readonly "password-reset": PublicTemplatePreview;
19
+ readonly receipt: PublicTemplatePreview;
20
+ readonly "payment-failed": PublicTemplatePreview;
21
+ readonly "shipping-update": PublicTemplatePreview;
22
+ readonly notification: PublicTemplatePreview;
23
+ readonly "team-invite": PublicTemplatePreview;
24
+ readonly "product-digest": PublicTemplatePreview;
25
+ readonly newsletter: PublicTemplatePreview;
26
+ readonly announcement: PublicTemplatePreview;
27
+ readonly promo: PublicTemplatePreview;
28
+ readonly "event-invite": PublicTemplatePreview;
29
+ readonly "membership-promo": PublicTemplatePreview;
30
+ readonly "onboarding-step": PublicTemplatePreview;
31
+ readonly "trial-ending": PublicTemplatePreview;
32
+ readonly "win-back": PublicTemplatePreview;
33
+ readonly "feedback-request": PublicTemplatePreview;
34
+ readonly "editorial-newsletter": PublicTemplatePreview;
35
+ readonly "member-referral-offer": PublicTemplatePreview;
36
+ readonly "catalog-sale-newsletter": PublicTemplatePreview;
37
+ readonly "limited-time-savings": PublicTemplatePreview;
38
+ readonly "rewards-milestone": PublicTemplatePreview;
39
+ readonly "order-delivered": PublicTemplatePreview;
40
+ readonly "travel-rewards-promo": PublicTemplatePreview;
41
+ readonly "events-roundup": PublicTemplatePreview;
42
+ readonly "product-onboarding-tour": PublicTemplatePreview;
43
+ readonly "product-updates-roundup": PublicTemplatePreview;
44
+ readonly "flash-getaway-offer": PublicTemplatePreview;
45
+ readonly "membership-benefits-promo": PublicTemplatePreview;
46
+ readonly "industry-news-digest": PublicTemplatePreview;
47
+ readonly "weekly-deals-circular": PublicTemplatePreview;
48
+ readonly "data-sharing-review": PublicTemplatePreview;
49
+ readonly "seasonal-sale-flyer": PublicTemplatePreview;
50
+ readonly "setup-checklist": PublicTemplatePreview;
51
+ readonly "feature-release-roundup": PublicTemplatePreview;
52
+ readonly "season-clearance-promo": PublicTemplatePreview;
53
+ };
54
+ //#endregion
55
+ export { PreviewQuality, PublicTemplatePreview, publicTemplatePreviews };
56
+ //# sourceMappingURL=public-preview.d.ts.map