@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.
- package/AGENTS.md +19 -0
- package/dist/{ast-63I-VLQx.d.ts → ast-BrDtApwS.d.ts} +34 -60
- package/dist/ast.d.ts +2 -2
- package/dist/ast.js +4 -52
- package/dist/{catalog-Ck1tAGwO.d.ts → catalog-60eaYC7b.d.ts} +2 -2
- package/dist/{checks-DXuERO4B.js → checks-DBweSIPN.js} +18 -20
- package/dist/checks.d.ts +1 -1
- package/dist/checks.js +1 -1
- package/dist/classes.d.ts +1 -1
- package/dist/compile-sms.d.ts +3 -3
- package/dist/compile-whatsapp.d.ts +5 -5
- package/dist/compile-whatsapp.js +3 -3
- package/dist/compile.d.ts +5 -5
- package/dist/compile.js +5 -5
- package/dist/compiler.d.ts +8 -5
- package/dist/compiler.js +45 -45
- package/dist/components.d.ts +4 -4
- package/dist/conditionals.js +56 -24
- package/dist/corpus-BjYMqOoY.d.ts +106 -0
- package/dist/{css-ar2lU3NC.d.ts → css-COcFOT1y.d.ts} +4 -5
- package/dist/diagnostics-BMYtBCQk.d.ts +46 -0
- package/dist/diagnostics-OcQKQYSY.d.ts +49 -0
- package/dist/diagnostics.d.ts +2 -2
- package/dist/diagnostics.js +24 -33
- package/dist/{elements-DtwKlFVH.d.ts → elements-BzjjnoCS.d.ts} +40 -161
- package/dist/email/components.d.ts +1 -1
- package/dist/email/components.js +40 -10
- package/dist/email/diagnostics.d.ts +1 -1
- package/dist/email/diagnostics.js +8 -43
- package/dist/email/elements.d.ts +1 -1
- package/dist/email/html.d.ts +2 -2
- package/dist/email/html.js +11 -10
- package/dist/email/jsx-dev-runtime.d.ts +3 -21
- package/dist/email/jsx-runtime.d.ts +1 -1
- package/dist/email/jsx-runtime.js +29 -3
- package/dist/email/render.d.ts +4 -4
- package/dist/email/render.js +1 -1
- package/dist/email/selection.d.ts +20 -31
- package/dist/email/selection.js +1 -53
- package/dist/email/source-edit.d.ts +1 -1
- package/dist/email/source-edit.js +2 -2
- package/dist/email/stylesheets.d.ts +1 -1
- package/dist/email/text.d.ts +1 -1
- package/dist/email/text.js +3 -3
- package/dist/emit.d.ts +2 -2
- package/dist/emit.js +5 -5
- package/dist/footer.js +7 -4
- package/dist/guards-eV1qLbf2.js +9 -0
- package/dist/{input-schema-BIfzI63B.d.ts → input-schema-7dPFVvNE.d.ts} +2 -2
- package/dist/input-schema-zjkgrXn2.js +455 -0
- package/dist/input-schema.d.ts +1 -1
- package/dist/input-schema.js +1 -169
- package/dist/interpolate.d.ts +9 -12
- package/dist/interpolate.js +1 -19
- package/dist/jsx-dev-runtime.d.ts +2 -2
- package/dist/{jsx-runtime-C5k_Zq0A.d.ts → jsx-runtime-I-1nCtl2.d.ts} +3 -3
- package/dist/jsx-runtime.d.ts +1 -1
- package/dist/jsx-runtime.js +3 -3
- package/dist/lexer.js +17 -5
- package/dist/model.d.ts +18 -33
- package/dist/model.js +4 -17
- package/dist/ops-2aiiYO7m.js +630 -0
- package/dist/ops.d.ts +78 -172
- package/dist/ops.js +2 -546
- package/dist/{parser-CGBS73Gz.d.ts → parser-CQcASEyy.d.ts} +5 -5
- package/dist/parser.d.ts +1 -1
- package/dist/parser.js +8 -8
- package/dist/{predicate-CvkzT-a2.js → predicate-BI6uRRM-.js} +24 -14
- package/dist/{predicate-G2SeSvoN.d.ts → predicate-M-05Nd7R.d.ts} +4 -4
- package/dist/predicate.d.ts +1 -1
- package/dist/predicate.js +1 -1
- package/dist/presets/category-mapping.d.ts +1 -1
- package/dist/presets/corpus.d.ts +1 -1
- package/dist/presets/corpus.js +74 -117
- package/dist/presets/public-gallery.d.ts +2 -2
- package/dist/presets/public-gallery.js +17 -9
- package/dist/presets/public-handoff.d.ts +4 -4
- package/dist/presets/public-handoff.js +10 -9
- package/dist/presets/public-preview-artifacts.d.ts +7 -5
- package/dist/presets/public-preview-artifacts.js +10 -9
- package/dist/presets/showcase.d.ts +1 -1
- package/dist/presets.d.ts +1 -1
- package/dist/printer.d.ts +1 -1
- package/dist/{render-ykQhtMs_.js → render-aaJBnsXK.js} +44 -27
- package/dist/render-template.d.ts +7 -7
- package/dist/render-template.js +6 -6
- package/dist/{showcase-C_xgl6hz.d.ts → showcase-DGZAWn4Z.d.ts} +2 -2
- package/dist/{source-locations-BCJQnIfU.d.ts → source-locations-DrCM82Ym.d.ts} +4 -26
- package/dist/source-locations.d.ts +2 -2
- package/dist/source-locations.js +3 -26
- package/dist/{template-Dy5yOPyp.d.ts → template-DWftqh4x.d.ts} +4 -4
- package/dist/template.d.ts +1 -1
- package/dist/template.js +3 -3
- package/dist/theme-css.d.ts +3 -3
- package/dist/theme-css.js +3 -3
- package/dist/theme.js +25 -2
- package/dist/tsx-source-instrumentation.d.ts +1 -3
- package/dist/tsx-source-instrumentation.js +6 -6
- package/dist/tsx-tracer.d.ts +7 -11
- package/dist/tsx-tracer.js +165 -165
- package/dist/{variables-if1VrV34.d.ts → variables-J_L3qxRa.d.ts} +2 -2
- package/dist/variables.d.ts +1 -1
- package/dist/variables.js +4 -4
- package/package.json +4 -3
- package/dist/corpus-U2EeALvS.d.ts +0 -234
- package/dist/diagnostics-Djd9ckLg.d.ts +0 -90
- package/dist/diagnostics-oveE-laV.d.ts +0 -79
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Result } from "
|
|
1
|
+
import { Result } from "better-result";
|
|
2
2
|
//#region src/limits.ts
|
|
3
3
|
/** Hard ceilings shared by SML documents and standalone predicate parsing. */
|
|
4
4
|
const SML_PARSER_LIMITS = {
|
|
@@ -46,7 +46,7 @@ const tokenize = (source, baseOffset) => {
|
|
|
46
46
|
const tokens = [];
|
|
47
47
|
let index = 0;
|
|
48
48
|
const length = source.length;
|
|
49
|
-
const failAt = (code, message, start, end) => Result.
|
|
49
|
+
const failAt = (code, message, start, end) => Result.err({ diagnostic: {
|
|
50
50
|
code,
|
|
51
51
|
message,
|
|
52
52
|
span: {
|
|
@@ -139,10 +139,13 @@ const tokenize = (source, baseOffset) => {
|
|
|
139
139
|
index++;
|
|
140
140
|
while (index < length && isDigit(source.charCodeAt(index))) index++;
|
|
141
141
|
}
|
|
142
|
+
const raw = source.slice(start, index);
|
|
143
|
+
const value = Number.parseFloat(raw);
|
|
144
|
+
if (!Number.isFinite(value)) return failAt("predicate-number-out-of-range", "number literal is outside the finite numeric range", start, index);
|
|
142
145
|
tokens.push({
|
|
143
146
|
kind: "number",
|
|
144
|
-
text:
|
|
145
|
-
number:
|
|
147
|
+
text: raw,
|
|
148
|
+
number: value,
|
|
146
149
|
span: {
|
|
147
150
|
start: baseOffset + start,
|
|
148
151
|
end: baseOffset + index
|
|
@@ -175,7 +178,7 @@ const tokenize = (source, baseOffset) => {
|
|
|
175
178
|
end: baseOffset + length
|
|
176
179
|
}
|
|
177
180
|
});
|
|
178
|
-
return Result.
|
|
181
|
+
return Result.ok(tokens);
|
|
179
182
|
};
|
|
180
183
|
const COMPARISON_OPS = [
|
|
181
184
|
"==",
|
|
@@ -191,7 +194,7 @@ const COMPARISON_OPS = [
|
|
|
191
194
|
* so diagnostic and node spans point into the containing document.
|
|
192
195
|
*/
|
|
193
196
|
const parsePredicate = (source, baseOffset = 0) => {
|
|
194
|
-
if (source.length > SML_PARSER_LIMITS.maxCharacters) return Result.
|
|
197
|
+
if (source.length > SML_PARSER_LIMITS.maxCharacters) return Result.err([{
|
|
195
198
|
code: "predicate-character-limit",
|
|
196
199
|
message: `predicate exceeds the ${SML_PARSER_LIMITS.maxCharacters} character limit`,
|
|
197
200
|
span: {
|
|
@@ -199,7 +202,7 @@ const parsePredicate = (source, baseOffset = 0) => {
|
|
|
199
202
|
end: baseOffset + source.length
|
|
200
203
|
}
|
|
201
204
|
}]);
|
|
202
|
-
if (new TextEncoder().encode(source).byteLength > SML_PARSER_LIMITS.maxUtf8Bytes) return Result.
|
|
205
|
+
if (new TextEncoder().encode(source).byteLength > SML_PARSER_LIMITS.maxUtf8Bytes) return Result.err([{
|
|
203
206
|
code: "predicate-byte-limit",
|
|
204
207
|
message: `predicate exceeds the ${SML_PARSER_LIMITS.maxUtf8Bytes} byte UTF-8 limit`,
|
|
205
208
|
span: {
|
|
@@ -208,8 +211,8 @@ const parsePredicate = (source, baseOffset = 0) => {
|
|
|
208
211
|
}
|
|
209
212
|
}]);
|
|
210
213
|
const lexed = tokenize(source, baseOffset);
|
|
211
|
-
if (Result.
|
|
212
|
-
const tokens = lexed.
|
|
214
|
+
if (Result.isError(lexed)) return Result.err([lexed.error.diagnostic]);
|
|
215
|
+
const tokens = lexed.value;
|
|
213
216
|
let index = 0;
|
|
214
217
|
let diagnostic;
|
|
215
218
|
let nodeCount = 0;
|
|
@@ -334,7 +337,7 @@ const parsePredicate = (source, baseOffset = 0) => {
|
|
|
334
337
|
}
|
|
335
338
|
return left;
|
|
336
339
|
};
|
|
337
|
-
if (source.trim().length === 0) return Result.
|
|
340
|
+
if (source.trim().length === 0) return Result.err([{
|
|
338
341
|
code: "predicate-empty",
|
|
339
342
|
message: "empty predicate — write e.g. plan == 'pro'",
|
|
340
343
|
span: {
|
|
@@ -347,7 +350,7 @@ const parsePredicate = (source, baseOffset = 0) => {
|
|
|
347
350
|
const trailing = peek();
|
|
348
351
|
if (trailing.kind !== "eof") fail("predicate-trailing-input", `unexpected ${JSON.stringify(trailing.text)} after the predicate — join clauses with "and" or "or"`, trailing.span);
|
|
349
352
|
}
|
|
350
|
-
if (ast === null || diagnostic !== void 0) return Result.
|
|
353
|
+
if (ast === null || diagnostic !== void 0) return Result.err([diagnostic ?? {
|
|
351
354
|
code: "predicate-parse-failed",
|
|
352
355
|
message: "predicate parse failed",
|
|
353
356
|
span: {
|
|
@@ -355,7 +358,7 @@ const parsePredicate = (source, baseOffset = 0) => {
|
|
|
355
358
|
end: baseOffset + source.length
|
|
356
359
|
}
|
|
357
360
|
}]);
|
|
358
|
-
return Result.
|
|
361
|
+
return Result.ok(ast);
|
|
359
362
|
};
|
|
360
363
|
const toNumber = (value) => {
|
|
361
364
|
if (typeof value === "number") return value;
|
|
@@ -449,7 +452,14 @@ const precedence = (node) => {
|
|
|
449
452
|
case "name": return 4;
|
|
450
453
|
}
|
|
451
454
|
};
|
|
452
|
-
const printLiteral = (literal) =>
|
|
455
|
+
const printLiteral = (literal) => {
|
|
456
|
+
if (literal.kind === "number") {
|
|
457
|
+
if (Number.isFinite(literal.value)) return String(literal.value);
|
|
458
|
+
throw new TypeError(`Number ${String(literal.value)} cannot be represented in a predicate: number literals must be finite`);
|
|
459
|
+
}
|
|
460
|
+
if (literal.value.includes("'") || literal.value.includes("--")) throw new TypeError(`String ${JSON.stringify(literal.value)} cannot be represented in a predicate: string literals cannot contain ' or --`);
|
|
461
|
+
return `'${literal.value}'`;
|
|
462
|
+
};
|
|
453
463
|
/** Canonical form: single space around operators/keywords, minimal parens. */
|
|
454
464
|
const printPredicate = (ast) => {
|
|
455
465
|
const frames = [{
|
|
@@ -536,4 +546,4 @@ const collectPredicateVariables = (ast) => {
|
|
|
536
546
|
//#endregion
|
|
537
547
|
export { printPredicate as a, parsePredicate as i, collectPredicateVariables as n, SML_PARSER_LIMITS as o, evaluatePredicate as r, PREDICATE_PARSER_LIMITS as t };
|
|
538
548
|
|
|
539
|
-
//# sourceMappingURL=predicate-
|
|
549
|
+
//# sourceMappingURL=predicate-BI6uRRM-.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Result } from "
|
|
1
|
+
import { a as Span, t as Diagnostic } from "./diagnostics-OcQKQYSY.js";
|
|
2
|
+
import { Result } from "better-result";
|
|
3
3
|
//#region src/predicate.d.ts
|
|
4
4
|
/** Standalone predicates inherit the same source, nesting, and node ceilings as SML. */
|
|
5
5
|
declare const PREDICATE_PARSER_LIMITS: {
|
|
@@ -46,11 +46,11 @@ type PredicateAst = {
|
|
|
46
46
|
* predicate's first character (an <If> test attribute passes valueSpan.start),
|
|
47
47
|
* so diagnostic and node spans point into the containing document.
|
|
48
48
|
*/
|
|
49
|
-
declare const parsePredicate: (source: string, baseOffset?: number) => Result
|
|
49
|
+
declare const parsePredicate: (source: string, baseOffset?: number) => Result<PredicateAst, ReadonlyArray<Diagnostic>>;
|
|
50
50
|
declare const evaluatePredicate: (ast: PredicateAst, variables: Record<string, unknown>) => boolean;
|
|
51
51
|
/** Canonical form: single space around operators/keywords, minimal parens. */
|
|
52
52
|
declare const printPredicate: (ast: PredicateAst) => string;
|
|
53
53
|
declare const collectPredicateVariables: (ast: PredicateAst) => Set<string>;
|
|
54
54
|
//#endregion
|
|
55
55
|
export { collectPredicateVariables as a, printPredicate as c, PredicateLiteral as i, PREDICATE_PARSER_LIMITS as n, evaluatePredicate as o, PredicateAst as r, parsePredicate as s, ComparisonOp as t };
|
|
56
|
-
//# sourceMappingURL=predicate-
|
|
56
|
+
//# sourceMappingURL=predicate-M-05Nd7R.d.ts.map
|
package/dist/predicate.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as collectPredicateVariables, c as printPredicate, i as PredicateLiteral, n as PREDICATE_PARSER_LIMITS, o as evaluatePredicate, r as PredicateAst, s as parsePredicate, t as ComparisonOp } from "./predicate-
|
|
1
|
+
import { a as collectPredicateVariables, c as printPredicate, i as PredicateLiteral, n as PREDICATE_PARSER_LIMITS, o as evaluatePredicate, r as PredicateAst, s as parsePredicate, t as ComparisonOp } from "./predicate-M-05Nd7R.js";
|
|
2
2
|
export { ComparisonOp, PREDICATE_PARSER_LIMITS, PredicateAst, PredicateLiteral, collectPredicateVariables, evaluatePredicate, parsePredicate, printPredicate };
|
package/dist/predicate.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as printPredicate, i as parsePredicate, n as collectPredicateVariables, r as evaluatePredicate, t as PREDICATE_PARSER_LIMITS } from "./predicate-
|
|
1
|
+
import { a as printPredicate, i as parsePredicate, n as collectPredicateVariables, r as evaluatePredicate, t as PREDICATE_PARSER_LIMITS } from "./predicate-BI6uRRM-.js";
|
|
2
2
|
export { PREDICATE_PARSER_LIMITS, collectPredicateVariables, evaluatePredicate, parsePredicate, printPredicate };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as PresetCategory } from "../catalog-
|
|
1
|
+
import { r as PresetCategory } from "../catalog-60eaYC7b.js";
|
|
2
2
|
import { TemplateCollectionId } from "./public-gallery.js";
|
|
3
3
|
//#region presets/category-mapping.d.ts
|
|
4
4
|
declare const LAB_DIMENSIONS: readonly ["newsletter", "receipt", "security", "product-update", "onboarding", "promotion", "shipping", "social-notification"];
|
package/dist/presets/corpus.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as PublicCorpusRegistrationIssue, c as TemplateFacetQuery, d as matchesTemplateFacets, f as ownedPresetSourcePath, h as templateFacetOptions, i as PublicCorpusRegistration, l as TemplateFacetSet, m as registerPublicCorpusEntry, n as InspirationReference, o as TemplateCorpusKind, p as ownedShowcasePresetSources, r as OwnedEditablePreset, s as TemplateCorpusRecord, t as BrandRecreationPreset, u as brandRecreationProvenance } from "../corpus-
|
|
1
|
+
import { a as PublicCorpusRegistrationIssue, c as TemplateFacetQuery, d as matchesTemplateFacets, f as ownedPresetSourcePath, h as templateFacetOptions, i as PublicCorpusRegistration, l as TemplateFacetSet, m as registerPublicCorpusEntry, n as InspirationReference, o as TemplateCorpusKind, p as ownedShowcasePresetSources, r as OwnedEditablePreset, s as TemplateCorpusRecord, t as BrandRecreationPreset, u as brandRecreationProvenance } from "../corpus-BjYMqOoY.js";
|
|
2
2
|
export { BrandRecreationPreset, InspirationReference, OwnedEditablePreset, PublicCorpusRegistration, PublicCorpusRegistrationIssue, TemplateCorpusKind, TemplateCorpusRecord, TemplateFacetQuery, TemplateFacetSet, brandRecreationProvenance, matchesTemplateFacets, ownedPresetSourcePath, ownedShowcasePresetSources, registerPublicCorpusEntry, templateFacetOptions };
|
package/dist/presets/corpus.js
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
+
import { n as isObject } from "../guards-eV1qLbf2.js";
|
|
1
2
|
import { presetCatalog } from "../presets.js";
|
|
2
3
|
import { showcaseProject, showcaseProjects, showcaseSourcePath } from "./showcase.js";
|
|
3
|
-
import { Result
|
|
4
|
+
import { Result } from "better-result";
|
|
4
5
|
//#region presets/corpus.ts
|
|
5
|
-
const TemplateCorpusKind = Schema.Literals([
|
|
6
|
-
"owned-editable-sml",
|
|
7
|
-
"brand-recreation-sml",
|
|
8
|
-
"inspiration-reference"
|
|
9
|
-
]);
|
|
10
6
|
const templateFacetOptions = {
|
|
11
7
|
jobType: [
|
|
12
8
|
"account-access",
|
|
@@ -47,74 +43,6 @@ const templateFacetOptions = {
|
|
|
47
43
|
],
|
|
48
44
|
brandProvenance: ["samva-original", "third-party-reference"]
|
|
49
45
|
};
|
|
50
|
-
const JobTypeFacet = Schema.Literals(templateFacetOptions.jobType);
|
|
51
|
-
const LifecycleFacet = Schema.Literals(templateFacetOptions.lifecycle);
|
|
52
|
-
const IndustryFacet = Schema.Literals(templateFacetOptions.industry);
|
|
53
|
-
const SeasonFacet = Schema.Literals(templateFacetOptions.season);
|
|
54
|
-
const AudienceFacet = Schema.Literals(templateFacetOptions.audience);
|
|
55
|
-
const DesignFacet = Schema.Literals(templateFacetOptions.design);
|
|
56
|
-
const BrandProvenanceFacet = Schema.Literals(templateFacetOptions.brandProvenance);
|
|
57
|
-
const TemplateFacetSet = Schema.Struct({
|
|
58
|
-
jobType: Schema.Array(JobTypeFacet),
|
|
59
|
-
lifecycle: Schema.Array(LifecycleFacet),
|
|
60
|
-
industry: Schema.Array(IndustryFacet),
|
|
61
|
-
season: Schema.Array(SeasonFacet),
|
|
62
|
-
audience: Schema.Array(AudienceFacet),
|
|
63
|
-
design: Schema.Array(DesignFacet),
|
|
64
|
-
brandProvenance: Schema.Array(BrandProvenanceFacet)
|
|
65
|
-
});
|
|
66
|
-
const CorpusReview = Schema.Struct({
|
|
67
|
-
state: Schema.Literals([
|
|
68
|
-
"draft",
|
|
69
|
-
"approved",
|
|
70
|
-
"rejected"
|
|
71
|
-
]),
|
|
72
|
-
reviewedBy: Schema.String,
|
|
73
|
-
reviewedAt: Schema.String,
|
|
74
|
-
notes: Schema.String
|
|
75
|
-
});
|
|
76
|
-
const CorpusDisplay = Schema.Struct({
|
|
77
|
-
state: Schema.Literals(["hidden", "public"]),
|
|
78
|
-
attributionLabel: Schema.String
|
|
79
|
-
});
|
|
80
|
-
const CorpusRetention = Schema.Struct({
|
|
81
|
-
policy: Schema.Literals(["retain-until-withdrawn", "retain-while-linked"]),
|
|
82
|
-
reviewedAt: Schema.String
|
|
83
|
-
});
|
|
84
|
-
const CorpusTakedown = Schema.Struct({
|
|
85
|
-
state: Schema.Literals(["active", "removed"]),
|
|
86
|
-
contact: Schema.String,
|
|
87
|
-
procedureUrl: Schema.String
|
|
88
|
-
});
|
|
89
|
-
const OwnedEditablePreset = Schema.TaggedStruct("OwnedEditablePreset", {
|
|
90
|
-
corpusKind: Schema.Literal("owned-editable-sml"),
|
|
91
|
-
provenance: Schema.Struct({
|
|
92
|
-
owner: Schema.Literal("Samva"),
|
|
93
|
-
sourcePath: Schema.String,
|
|
94
|
-
recordedAt: Schema.String
|
|
95
|
-
}),
|
|
96
|
-
review: CorpusReview,
|
|
97
|
-
display: CorpusDisplay,
|
|
98
|
-
retention: CorpusRetention,
|
|
99
|
-
takedown: CorpusTakedown
|
|
100
|
-
});
|
|
101
|
-
const BrandRecreationPreset = Schema.TaggedStruct("BrandRecreationPreset", {
|
|
102
|
-
corpusKind: Schema.Literal("brand-recreation-sml"),
|
|
103
|
-
provenance: Schema.Struct({
|
|
104
|
-
owner: Schema.Literal("Samva"),
|
|
105
|
-
sourcePath: Schema.String,
|
|
106
|
-
recordedAt: Schema.String,
|
|
107
|
-
/** The brand whose own email is recreated, named on every public surface. */
|
|
108
|
-
brand: Schema.String,
|
|
109
|
-
/** What that brand's email does, in the customer's words. */
|
|
110
|
-
subject: Schema.String,
|
|
111
|
-
reviewedNote: Schema.String
|
|
112
|
-
}),
|
|
113
|
-
review: CorpusReview,
|
|
114
|
-
display: CorpusDisplay,
|
|
115
|
-
retention: CorpusRetention,
|
|
116
|
-
takedown: CorpusTakedown
|
|
117
|
-
});
|
|
118
46
|
/** Clean-room recreations of a named brand's own email, authored as editable Samva projects. */
|
|
119
47
|
const brandRecreationProvenance = Object.fromEntries(showcaseProjects.flatMap((project) => project.provenance.kind === "brand-recreation" ? [[project.id, {
|
|
120
48
|
owner: "Samva",
|
|
@@ -133,24 +61,6 @@ const ownedPresetSourcePath = (id) => {
|
|
|
133
61
|
const project = showcaseProject(id);
|
|
134
62
|
return project === void 0 || project.provenance.kind !== "owned" ? `packages/markup/presets/${id}.tsx` : showcaseSourcePath(project);
|
|
135
63
|
};
|
|
136
|
-
const InspirationReference = Schema.TaggedStruct("InspirationReference", {
|
|
137
|
-
corpusKind: Schema.Literal("inspiration-reference"),
|
|
138
|
-
provenance: Schema.Struct({
|
|
139
|
-
publisher: Schema.String,
|
|
140
|
-
sourceUrl: Schema.String,
|
|
141
|
-
capturedAt: Schema.String,
|
|
142
|
-
rightsBasis: Schema.Literals(["display-link-and-commentary", "permission"])
|
|
143
|
-
}),
|
|
144
|
-
review: CorpusReview,
|
|
145
|
-
display: CorpusDisplay,
|
|
146
|
-
retention: CorpusRetention,
|
|
147
|
-
takedown: CorpusTakedown
|
|
148
|
-
});
|
|
149
|
-
const TemplateCorpusRecord = Schema.Union([
|
|
150
|
-
OwnedEditablePreset,
|
|
151
|
-
BrandRecreationPreset,
|
|
152
|
-
InspirationReference
|
|
153
|
-
]);
|
|
154
64
|
const ISO_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;
|
|
155
65
|
const nonEmpty = (value) => value.trim().length > 0;
|
|
156
66
|
const isHttpsUrl = (value) => {
|
|
@@ -166,65 +76,112 @@ const requiredFacetDimensions = [
|
|
|
166
76
|
"design",
|
|
167
77
|
"brandProvenance"
|
|
168
78
|
];
|
|
79
|
+
/** Exact key sets: a record carrying anything else is not the shape this gate admits. */
|
|
80
|
+
const exactKeys = (value, keys) => isObject(value) && Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
|
|
81
|
+
const isStringIn = (value, values) => typeof value === "string" && values.includes(value);
|
|
82
|
+
const isStringArrayIn = (value, values) => Array.isArray(value) && value.every((entry) => isStringIn(entry, values));
|
|
83
|
+
const isReview = (value) => exactKeys(value, [
|
|
84
|
+
"state",
|
|
85
|
+
"reviewedBy",
|
|
86
|
+
"reviewedAt",
|
|
87
|
+
"notes"
|
|
88
|
+
]) && isStringIn(value.state, [
|
|
89
|
+
"draft",
|
|
90
|
+
"approved",
|
|
91
|
+
"rejected"
|
|
92
|
+
]) && typeof value.reviewedBy === "string" && typeof value.reviewedAt === "string" && typeof value.notes === "string";
|
|
93
|
+
const isDisplay = (value) => exactKeys(value, ["state", "attributionLabel"]) && isStringIn(value.state, ["hidden", "public"]) && typeof value.attributionLabel === "string";
|
|
94
|
+
const isRetention = (value) => exactKeys(value, ["policy", "reviewedAt"]) && isStringIn(value.policy, ["retain-until-withdrawn", "retain-while-linked"]) && typeof value.reviewedAt === "string";
|
|
95
|
+
const isTakedown = (value) => exactKeys(value, [
|
|
96
|
+
"state",
|
|
97
|
+
"contact",
|
|
98
|
+
"procedureUrl"
|
|
99
|
+
]) && isStringIn(value.state, ["active", "removed"]) && typeof value.contact === "string" && typeof value.procedureUrl === "string";
|
|
100
|
+
/** The four governance blocks every corpus record carries, whatever its kind. */
|
|
101
|
+
const hasGovernance = (value) => isReview(value.review) && isDisplay(value.display) && isRetention(value.retention) && isTakedown(value.takedown);
|
|
102
|
+
const CORPUS_KEYS = [
|
|
103
|
+
"type",
|
|
104
|
+
"corpusKind",
|
|
105
|
+
"provenance",
|
|
106
|
+
"review",
|
|
107
|
+
"display",
|
|
108
|
+
"retention",
|
|
109
|
+
"takedown"
|
|
110
|
+
];
|
|
111
|
+
const isCorpusRecord = (value) => {
|
|
112
|
+
if (!exactKeys(value, CORPUS_KEYS) || !hasGovernance(value)) return false;
|
|
113
|
+
const provenance = value.provenance;
|
|
114
|
+
if (value.type === "OwnedEditablePreset") return value.corpusKind === "owned-editable-sml" && exactKeys(provenance, [
|
|
115
|
+
"owner",
|
|
116
|
+
"sourcePath",
|
|
117
|
+
"recordedAt"
|
|
118
|
+
]) && provenance.owner === "Samva" && typeof provenance.sourcePath === "string" && typeof provenance.recordedAt === "string";
|
|
119
|
+
if (value.type === "BrandRecreationPreset") return value.corpusKind === "brand-recreation-sml" && exactKeys(provenance, [
|
|
120
|
+
"owner",
|
|
121
|
+
"sourcePath",
|
|
122
|
+
"recordedAt",
|
|
123
|
+
"brand",
|
|
124
|
+
"subject",
|
|
125
|
+
"reviewedNote"
|
|
126
|
+
]) && provenance.owner === "Samva" && typeof provenance.sourcePath === "string" && typeof provenance.recordedAt === "string" && typeof provenance.brand === "string" && typeof provenance.subject === "string" && typeof provenance.reviewedNote === "string";
|
|
127
|
+
return value.type === "InspirationReference" && value.corpusKind === "inspiration-reference" && exactKeys(provenance, [
|
|
128
|
+
"publisher",
|
|
129
|
+
"sourceUrl",
|
|
130
|
+
"capturedAt",
|
|
131
|
+
"rightsBasis"
|
|
132
|
+
]) && typeof provenance.publisher === "string" && typeof provenance.sourceUrl === "string" && typeof provenance.capturedAt === "string" && isStringIn(provenance.rightsBasis, ["display-link-and-commentary", "permission"]);
|
|
133
|
+
};
|
|
134
|
+
const isFacetSet = (value) => exactKeys(value, requiredFacetDimensions) && requiredFacetDimensions.every((dimension) => isStringArrayIn(value[dimension], templateFacetOptions[dimension]));
|
|
135
|
+
const isCorpusEntry = (value) => exactKeys(value, [
|
|
136
|
+
"id",
|
|
137
|
+
"corpus",
|
|
138
|
+
"facets"
|
|
139
|
+
]) && typeof value.id === "string" && isCorpusRecord(value.corpus) && isFacetSet(value.facets);
|
|
169
140
|
const canonicalOwnedPresetSources = new Map(presetCatalog.filter((preset) => preset.channel === "email" && !Object.hasOwn(brandRecreationProvenance, preset.id)).map((preset) => [preset.id, ownedPresetSourcePath(preset.id)]));
|
|
170
141
|
/** Decode and gate a record before it can enter any public projection. */
|
|
171
142
|
const registerPublicCorpusEntry = (input) => {
|
|
172
|
-
|
|
173
|
-
id: Schema.String,
|
|
174
|
-
corpus: TemplateCorpusRecord,
|
|
175
|
-
facets: TemplateFacetSet
|
|
176
|
-
}), { onExcessProperty: "error" })(input);
|
|
177
|
-
if (Result.isFailure(decoded)) return Result.fail({
|
|
143
|
+
if (!isCorpusEntry(input)) return Result.err({
|
|
178
144
|
id: "unknown",
|
|
179
145
|
message: "record does not match a corpus schema"
|
|
180
146
|
});
|
|
181
|
-
const { id, corpus, facets } =
|
|
147
|
+
const { id, corpus, facets } = input;
|
|
182
148
|
const timestamps = [
|
|
183
149
|
corpus.review.reviewedAt,
|
|
184
150
|
corpus.retention.reviewedAt,
|
|
185
|
-
corpus.
|
|
151
|
+
corpus.type === "InspirationReference" ? corpus.provenance.capturedAt : corpus.provenance.recordedAt
|
|
186
152
|
];
|
|
187
|
-
if (!nonEmpty(id) || corpus.review.state !== "approved" || !nonEmpty(corpus.review.reviewedBy) || !nonEmpty(corpus.review.notes) || corpus.display.state !== "public" || !nonEmpty(corpus.display.attributionLabel) || corpus.takedown.state !== "active" || !nonEmpty(corpus.takedown.contact) || !isHttpsUrl(corpus.takedown.procedureUrl) || timestamps.some((timestamp) => !ISO_TIMESTAMP.test(timestamp))) return Result.
|
|
153
|
+
if (!nonEmpty(id) || corpus.review.state !== "approved" || !nonEmpty(corpus.review.reviewedBy) || !nonEmpty(corpus.review.notes) || corpus.display.state !== "public" || !nonEmpty(corpus.display.attributionLabel) || corpus.takedown.state !== "active" || !nonEmpty(corpus.takedown.contact) || !isHttpsUrl(corpus.takedown.procedureUrl) || timestamps.some((timestamp) => !ISO_TIMESTAMP.test(timestamp))) return Result.err({
|
|
188
154
|
id,
|
|
189
155
|
message: "public corpus records require approved review, display, retention, and takedown metadata"
|
|
190
156
|
});
|
|
191
|
-
if (corpus.
|
|
157
|
+
if (corpus.type === "OwnedEditablePreset" && (canonicalOwnedPresetSources.get(id) !== corpus.provenance.sourcePath || corpus.retention.policy !== "retain-until-withdrawn" || facets.brandProvenance.some((value) => value !== "samva-original"))) return Result.err({
|
|
192
158
|
id,
|
|
193
159
|
message: "owned presets require a canonical editable TSX source"
|
|
194
160
|
});
|
|
195
|
-
if (corpus.
|
|
161
|
+
if (corpus.type === "BrandRecreationPreset") {
|
|
196
162
|
const canonical = Object.entries(brandRecreationProvenance).find(([presetId]) => presetId === id)?.[1];
|
|
197
|
-
if (canonical === void 0 || Object.entries(canonical).some(([key, value]) => corpus.provenance[key] !== value) || !nonEmpty(corpus.provenance.brand) || !nonEmpty(corpus.provenance.subject) || corpus.retention.policy !== "retain-until-withdrawn" || facets.brandProvenance.some((value) => value !== "third-party-reference")) return Result.
|
|
163
|
+
if (canonical === void 0 || Object.entries(canonical).some(([key, value]) => corpus.provenance[key] !== value) || !nonEmpty(corpus.provenance.brand) || !nonEmpty(corpus.provenance.subject) || corpus.retention.policy !== "retain-until-withdrawn" || facets.brandProvenance.some((value) => value !== "third-party-reference")) return Result.err({
|
|
198
164
|
id,
|
|
199
165
|
message: "brand recreations require a canonical source, a referenced brand and subject, and third-party provenance"
|
|
200
166
|
});
|
|
201
167
|
}
|
|
202
|
-
if (corpus.
|
|
168
|
+
if (corpus.type === "InspirationReference" && (!isHttpsUrl(corpus.provenance.sourceUrl) || corpus.retention.policy !== "retain-while-linked" || facets.brandProvenance.some((value) => value !== "third-party-reference"))) return Result.err({
|
|
203
169
|
id,
|
|
204
170
|
message: "inspiration references require an HTTPS source and third-party provenance facet"
|
|
205
171
|
});
|
|
206
172
|
for (const dimension of requiredFacetDimensions) {
|
|
207
173
|
const values = facets[dimension];
|
|
208
|
-
if (values.length === 0 || new Set(values).size !== values.length) return Result.
|
|
174
|
+
if (values.length === 0 || new Set(values).size !== values.length) return Result.err({
|
|
209
175
|
id,
|
|
210
176
|
message: `facet ${dimension} must be populated without duplicates`
|
|
211
177
|
});
|
|
212
178
|
}
|
|
213
|
-
return Result.
|
|
179
|
+
return Result.ok({
|
|
214
180
|
id,
|
|
215
181
|
corpus,
|
|
216
182
|
facets
|
|
217
183
|
});
|
|
218
184
|
};
|
|
219
|
-
const TemplateFacetQuery = Schema.Struct({
|
|
220
|
-
jobType: Schema.optionalKey(Schema.Array(JobTypeFacet)),
|
|
221
|
-
lifecycle: Schema.optionalKey(Schema.Array(LifecycleFacet)),
|
|
222
|
-
industry: Schema.optionalKey(Schema.Array(IndustryFacet)),
|
|
223
|
-
season: Schema.optionalKey(Schema.Array(SeasonFacet)),
|
|
224
|
-
audience: Schema.optionalKey(Schema.Array(AudienceFacet)),
|
|
225
|
-
design: Schema.optionalKey(Schema.Array(DesignFacet)),
|
|
226
|
-
brandProvenance: Schema.optionalKey(Schema.Array(BrandProvenanceFacet))
|
|
227
|
-
});
|
|
228
185
|
/** Strict intersection: every selected value in every selected dimension must match. */
|
|
229
186
|
const matchesTemplateFacets = (facets, query) => requiredFacetDimensions.every((dimension) => {
|
|
230
187
|
const selected = query[dimension];
|
|
@@ -232,6 +189,6 @@ const matchesTemplateFacets = (facets, query) => requiredFacetDimensions.every((
|
|
|
232
189
|
return selected === void 0 || selected.every((value) => available.has(value));
|
|
233
190
|
});
|
|
234
191
|
//#endregion
|
|
235
|
-
export {
|
|
192
|
+
export { brandRecreationProvenance, matchesTemplateFacets, ownedPresetSourcePath, ownedShowcasePresetSources, registerPublicCorpusEntry, templateFacetOptions };
|
|
236
193
|
|
|
237
194
|
//# sourceMappingURL=corpus.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as EmailPresetId } from "../catalog-
|
|
2
|
-
import { c as TemplateFacetQuery, l as TemplateFacetSet, r as OwnedEditablePreset, t as BrandRecreationPreset } from "../corpus-
|
|
1
|
+
import { t as EmailPresetId } from "../catalog-60eaYC7b.js";
|
|
2
|
+
import { c as TemplateFacetQuery, l as TemplateFacetSet, r as OwnedEditablePreset, t as BrandRecreationPreset } from "../corpus-BjYMqOoY.js";
|
|
3
3
|
//#region presets/public-gallery.d.ts
|
|
4
4
|
declare const templateUseCases: readonly ["account-access", "commerce", "product-communication", "lifecycle", "publishing-campaigns"];
|
|
5
5
|
declare const templateCollectionFamilies: readonly ["category", "industry", "stage", "style"];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { brandRecreationProvenance, matchesTemplateFacets, registerPublicCorpusEntry } from "./corpus.js";
|
|
2
|
-
import { Result } from "
|
|
2
|
+
import { Result } from "better-result";
|
|
3
3
|
//#region presets/public-gallery.ts
|
|
4
4
|
const templateUseCases = [
|
|
5
5
|
"account-access",
|
|
@@ -14,7 +14,7 @@ const templateCollectionFamilies = [
|
|
|
14
14
|
"stage",
|
|
15
15
|
"style"
|
|
16
16
|
];
|
|
17
|
-
const isMadeWithSamvaTemplate = (template) => template.corpus.
|
|
17
|
+
const isMadeWithSamvaTemplate = (template) => template.corpus.type === "OwnedEditablePreset" || template.corpus.type === "BrandRecreationPreset";
|
|
18
18
|
/** Explicit evidence for every owned preset reviewed in the 2026-08-31 catalog pass. */
|
|
19
19
|
const ownedPresetReviewManifest = {
|
|
20
20
|
welcome: {
|
|
@@ -260,7 +260,7 @@ const template = (value) => {
|
|
|
260
260
|
if (isBrandRecreationPresetId(value.presetId)) {
|
|
261
261
|
const provenance = brandRecreationProvenance[value.presetId];
|
|
262
262
|
return {
|
|
263
|
-
|
|
263
|
+
type: "BrandRecreationPreset",
|
|
264
264
|
corpusKind: "brand-recreation-sml",
|
|
265
265
|
provenance,
|
|
266
266
|
review: {
|
|
@@ -286,7 +286,7 @@ const template = (value) => {
|
|
|
286
286
|
}
|
|
287
287
|
const review = ownedPresetReviewManifest[value.presetId];
|
|
288
288
|
return {
|
|
289
|
-
|
|
289
|
+
type: "OwnedEditablePreset",
|
|
290
290
|
corpusKind: "owned-editable-sml",
|
|
291
291
|
provenance: {
|
|
292
292
|
owner: "Samva",
|
|
@@ -321,19 +321,19 @@ const template = (value) => {
|
|
|
321
321
|
season: value.presetId === "seasonal-sale-flyer" || value.presetId === "season-clearance-promo" ? ["seasonal"] : ["evergreen"],
|
|
322
322
|
audience: audienceFor(value.useCase),
|
|
323
323
|
design: facetValues(value.collections, "style"),
|
|
324
|
-
brandProvenance: [corpus.
|
|
324
|
+
brandProvenance: [corpus.type === "BrandRecreationPreset" ? "third-party-reference" : "samva-original"]
|
|
325
325
|
};
|
|
326
326
|
const registration = registerPublicCorpusEntry({
|
|
327
327
|
id: value.presetId,
|
|
328
328
|
corpus,
|
|
329
329
|
facets
|
|
330
330
|
});
|
|
331
|
-
if (Result.
|
|
331
|
+
if (Result.isError(registration)) throw new Error(`${registration.error.id}: ${registration.error.message}`);
|
|
332
332
|
return {
|
|
333
333
|
...value,
|
|
334
334
|
path: `/templates/${value.slug}`,
|
|
335
335
|
corpus,
|
|
336
|
-
facets: registration.
|
|
336
|
+
facets: registration.value.facets
|
|
337
337
|
};
|
|
338
338
|
};
|
|
339
339
|
const publicTemplates = [
|
|
@@ -362,7 +362,11 @@ const publicTemplates = [
|
|
|
362
362
|
"style:minimal",
|
|
363
363
|
"style:single-cta"
|
|
364
364
|
],
|
|
365
|
-
relatedTemplates: [
|
|
365
|
+
relatedTemplates: [
|
|
366
|
+
"otp",
|
|
367
|
+
"password-reset",
|
|
368
|
+
"welcome"
|
|
369
|
+
]
|
|
366
370
|
}),
|
|
367
371
|
template({
|
|
368
372
|
presetId: "otp",
|
|
@@ -375,7 +379,11 @@ const publicTemplates = [
|
|
|
375
379
|
"stage:activation",
|
|
376
380
|
"style:minimal"
|
|
377
381
|
],
|
|
378
|
-
relatedTemplates: [
|
|
382
|
+
relatedTemplates: [
|
|
383
|
+
"email-verification",
|
|
384
|
+
"password-reset",
|
|
385
|
+
"linear-login-code"
|
|
386
|
+
]
|
|
379
387
|
}),
|
|
380
388
|
template({
|
|
381
389
|
presetId: "password-reset",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { r as PresetCategory } from "../catalog-
|
|
1
|
+
import { l as Channel } from "../ast-BrDtApwS.js";
|
|
2
|
+
import { r as PresetCategory } from "../catalog-60eaYC7b.js";
|
|
3
3
|
import { TemplateCollectionId, TemplateUseCase } from "./public-gallery.js";
|
|
4
4
|
import { PublicTemplatePreview } from "./public-preview.js";
|
|
5
5
|
import { PresetProject } from "./sources.js";
|
|
6
|
-
import { Result } from "
|
|
6
|
+
import { Result } from "better-result";
|
|
7
7
|
//#region presets/public-handoff.d.ts
|
|
8
8
|
/** The brand a clean-room recreation names, and what its email does. */
|
|
9
9
|
interface TemplateBrandRecreation {
|
|
@@ -158,7 +158,7 @@ declare const emitTemplateReferencePacket: (packet: TemplateReferencePacket) =>
|
|
|
158
158
|
* the existing preset, public-gallery, preview, and editorial contracts; this
|
|
159
159
|
* function never registers or publishes it.
|
|
160
160
|
*/
|
|
161
|
-
declare const validatePublicTemplateHandoff: (artifacts: ReadonlyArray<ApprovedPublicTemplateArtifact>) => Result
|
|
161
|
+
declare const validatePublicTemplateHandoff: (artifacts: ReadonlyArray<ApprovedPublicTemplateArtifact>) => Result<ReadonlyArray<ValidatedPublicTemplateArtifact>, ReadonlyArray<PublicHandoffIssue>>;
|
|
162
162
|
//#endregion
|
|
163
163
|
export { ApprovedPublicTemplateArtifact, PUBLIC_TEMPLATE_PROJECT_FILE_MAX_BYTES, PUBLIC_TEMPLATE_PROJECT_MAX_BYTES, PUBLIC_TEMPLATE_PROJECT_MAX_FILES, PUBLIC_TEMPLATE_SOURCE_MAX_CHARACTERS, PublicHandoffIssue, PublicHandoffIssueCode, PublicTemplateAuthoredStructure, TemplateBrandRecreation, TemplateReferencePacket, TemplateReferenceProjection, TemplateReferenceProvenance, ValidatedPublicTemplateArtifact, emitTemplateReferencePacket, isSafePublicTemplateAsset, isSafePublicTemplateProject, isSafePublicTemplateProjectPath, isValidTemplateBrandRecreation, publicTemplateAuthoredStructure, publicTemplateProjectDigest, toTemplateReferencePacket, unsupportedPublicTemplateFontTokens, validatePublicTemplateHandoff };
|
|
164
164
|
//# sourceMappingURL=public-handoff.d.ts.map
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { n as isObject } from "../guards-eV1qLbf2.js";
|
|
1
2
|
import { EMAIL_ELEMENTS } from "../email/elements.js";
|
|
2
3
|
import { presetCatalog } from "../presets.js";
|
|
3
4
|
import { publicTemplates, templateCollections } from "./public-gallery.js";
|
|
4
|
-
import {
|
|
5
|
+
import { Result } from "better-result";
|
|
5
6
|
import { parse } from "@babel/parser";
|
|
6
7
|
//#region presets/public-handoff.ts
|
|
7
8
|
const SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
@@ -15,7 +16,7 @@ const BLOCKED_PUBLIC_ASSET = /(?:unsplash|placehold|assets\.example)/i;
|
|
|
15
16
|
const nonEmptySample = (value) => {
|
|
16
17
|
if (typeof value === "string") return nonEmpty(value);
|
|
17
18
|
if (Array.isArray(value)) return value.length > 0 && value.every(nonEmptySample);
|
|
18
|
-
if (
|
|
19
|
+
if (isObject(value)) {
|
|
19
20
|
const values = Object.values(value);
|
|
20
21
|
return values.length > 0 && values.every(nonEmptySample);
|
|
21
22
|
}
|
|
@@ -120,13 +121,13 @@ const publicTemplateAuthoredStructure = (source) => {
|
|
|
120
121
|
for (const item of value) walk(item, depth);
|
|
121
122
|
return;
|
|
122
123
|
}
|
|
123
|
-
if (!
|
|
124
|
+
if (!isObject(value)) return;
|
|
124
125
|
let childDepth = depth;
|
|
125
126
|
if (value.type === "JSXElement") {
|
|
126
127
|
const opening = value.openingElement;
|
|
127
|
-
if (!
|
|
128
|
+
if (!isObject(opening)) return;
|
|
128
129
|
const name = opening.name;
|
|
129
|
-
const tag =
|
|
130
|
+
const tag = isObject(name) && typeof name.name === "string" && PUBLIC_COMPONENT_TAGS.has(name.name) ? name.name : "Component";
|
|
130
131
|
const childIndex = siblings.get(depth) ?? 0;
|
|
131
132
|
siblings.set(depth, childIndex + 1);
|
|
132
133
|
siblings.set(depth + 1, 0);
|
|
@@ -137,9 +138,9 @@ const publicTemplateAuthoredStructure = (source) => {
|
|
|
137
138
|
});
|
|
138
139
|
counts.set(tag, (counts.get(tag) ?? 0) + 1);
|
|
139
140
|
if (Array.isArray(opening.attributes)) for (const attribute of opening.attributes) {
|
|
140
|
-
if (!
|
|
141
|
+
if (!isObject(attribute) || !isObject(attribute.name) || attribute.name.name !== "className") continue;
|
|
141
142
|
const literal = attribute.value;
|
|
142
|
-
if (!
|
|
143
|
+
if (!isObject(literal) || literal.type !== "StringLiteral" || typeof literal.value !== "string") continue;
|
|
143
144
|
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()}`);
|
|
144
145
|
}
|
|
145
146
|
childDepth += 1;
|
|
@@ -329,8 +330,8 @@ const validatePublicTemplateHandoff = (artifacts) => {
|
|
|
329
330
|
if (new Set(artifact.gallery.relatedTemplates).size !== artifact.gallery.relatedTemplates.length) issues.push(issue(id, "reference", "related template references must be unique"));
|
|
330
331
|
for (const relatedId of artifact.gallery.relatedTemplates) if (relatedId === id || !allKnownIds.has(relatedId)) issues.push(issue(id, "reference", `related template "${relatedId}" is invalid`));
|
|
331
332
|
}
|
|
332
|
-
if (issues.length > 0) return Result.
|
|
333
|
-
return Result.
|
|
333
|
+
if (issues.length > 0) return Result.err(issues);
|
|
334
|
+
return Result.ok(artifacts.map((artifact) => {
|
|
334
335
|
const reference = toTemplateReferencePacket(artifact);
|
|
335
336
|
return {
|
|
336
337
|
approval: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { t as EmailPresetId } from "../catalog-
|
|
1
|
+
import { t as EmailPresetId } from "../catalog-60eaYC7b.js";
|
|
2
2
|
import { PublicTemplate } from "./public-gallery.js";
|
|
3
3
|
import { PreviewQuality } from "./public-preview.js";
|
|
4
|
-
import { Result } from "
|
|
4
|
+
import { Result } from "better-result";
|
|
5
5
|
//#region presets/public-preview-artifacts.d.ts
|
|
6
6
|
type PreviewSurface = "card" | "detail" | "og";
|
|
7
7
|
type PreviewViewport = "desktop" | "mobile";
|
|
@@ -23,6 +23,8 @@ interface PublicTemplatePreviewArtifact {
|
|
|
23
23
|
readonly quality: PreviewQuality;
|
|
24
24
|
readonly source: string;
|
|
25
25
|
readonly html: string;
|
|
26
|
+
/** The delivered plain-text alternative of the same executed fixture. */
|
|
27
|
+
readonly text: string;
|
|
26
28
|
readonly assets: ReadonlyArray<string>;
|
|
27
29
|
readonly renders: ReadonlyArray<PublicTemplateRenderInput>;
|
|
28
30
|
}
|
|
@@ -31,11 +33,11 @@ interface PreviewArtifactBuildIssue {
|
|
|
31
33
|
readonly message: string;
|
|
32
34
|
}
|
|
33
35
|
/** Build one static preview artifact without loading the editor runtime or performing I/O. */
|
|
34
|
-
declare const buildPublicTemplatePreviewArtifact: (template: PublicTemplate) => Result
|
|
36
|
+
declare const buildPublicTemplatePreviewArtifact: (template: PublicTemplate) => Result<PublicTemplatePreviewArtifact, PreviewArtifactBuildIssue>;
|
|
35
37
|
/** Build only the requested canonical subset, preserving manifest order and aggregated failures. */
|
|
36
|
-
declare const buildPublicTemplatePreviewArtifactsFor: (templates: ReadonlyArray<PublicTemplate>) => Result
|
|
38
|
+
declare const buildPublicTemplatePreviewArtifactsFor: (templates: ReadonlyArray<PublicTemplate>) => Result<ReadonlyArray<PublicTemplatePreviewArtifact>, ReadonlyArray<PreviewArtifactBuildIssue>>;
|
|
37
39
|
/** Build the complete canonical public gallery. */
|
|
38
|
-
declare const buildPublicTemplatePreviewArtifacts: () => Result
|
|
40
|
+
declare const buildPublicTemplatePreviewArtifacts: () => Result<readonly PublicTemplatePreviewArtifact[], readonly PreviewArtifactBuildIssue[]>;
|
|
39
41
|
//#endregion
|
|
40
42
|
export { PreviewArtifactBuildIssue, PreviewColorScheme, PreviewSurface, PreviewViewport, PublicTemplatePreviewArtifact, PublicTemplateRenderInput, buildPublicTemplatePreviewArtifact, buildPublicTemplatePreviewArtifacts, buildPublicTemplatePreviewArtifactsFor };
|
|
41
43
|
//# sourceMappingURL=public-preview-artifacts.d.ts.map
|