@diviops/mcp-server 1.5.11 → 1.5.13
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/README.md +20 -2
- package/dist/preset-cli/__tests__/cli.test.js +385 -0
- package/dist/preset-cli/__tests__/heading-font-emitter.test.d.ts +12 -0
- package/dist/preset-cli/__tests__/heading-font-emitter.test.js +249 -0
- package/dist/preset-cli/__tests__/registry.test.js +41 -0
- package/dist/preset-cli/__tests__/text-body-font-emitter.test.d.ts +14 -0
- package/dist/preset-cli/__tests__/text-body-font-emitter.test.js +191 -0
- package/dist/preset-cli/__tests__/write-path.test.js +110 -1
- package/dist/preset-cli/cli.d.ts +6 -0
- package/dist/preset-cli/cli.js +198 -11
- package/dist/preset-cli/heading-font-emitter.d.ts +128 -0
- package/dist/preset-cli/heading-font-emitter.js +166 -0
- package/dist/preset-cli/registry.d.ts +23 -9
- package/dist/preset-cli/registry.js +37 -13
- package/dist/preset-cli/text-body-font-emitter.d.ts +127 -0
- package/dist/preset-cli/text-body-font-emitter.js +169 -0
- package/dist/preset-cli/write-path.d.ts +31 -0
- package/dist/preset-cli/write-path.js +43 -0
- package/package.json +1 -1
|
@@ -77,28 +77,48 @@ function levelName(registry, num) {
|
|
|
77
77
|
return "UNVERIFIED";
|
|
78
78
|
}
|
|
79
79
|
/**
|
|
80
|
-
* Find a Tier 1 / Tier 2 entry by `pattern_family
|
|
81
|
-
*
|
|
80
|
+
* Find a Tier 1 / Tier 2 entry by `pattern_family` and (optionally)
|
|
81
|
+
* `pattern_variant`. Tier 2 is searched first, then Tier 1.
|
|
82
|
+
*
|
|
83
|
+
* Some pattern families (e.g. `divi/font`) carry multiple entries that
|
|
84
|
+
* differ ONLY by `pattern_variant` (`google_fonts_pattern_a` vs
|
|
85
|
+
* `local_hosted_pattern_b`). When `patternVariant` is supplied, ONLY an
|
|
86
|
+
* entry whose `pattern_variant` matches exactly is returned — a Pattern-A
|
|
87
|
+
* entry must not vouch for a Pattern-B caller, and vice versa.
|
|
88
|
+
*
|
|
89
|
+
* When `patternVariant` is omitted, the legacy behavior holds: the first
|
|
90
|
+
* matching `pattern_family` is returned regardless of variant — only safe
|
|
91
|
+
* for families with a single entry. New variant-aware callers should pass
|
|
92
|
+
* the variant explicitly.
|
|
82
93
|
*/
|
|
83
|
-
export function findPatternEntry(registry, patternFamily) {
|
|
94
|
+
export function findPatternEntry(registry, patternFamily, patternVariant) {
|
|
84
95
|
const tiers = [registry.tier2 ?? [], registry.tier1 ?? []];
|
|
85
96
|
for (const tier of tiers) {
|
|
86
|
-
const hit = tier.find((e) =>
|
|
97
|
+
const hit = tier.find((e) => {
|
|
98
|
+
if (e.pattern_family !== patternFamily)
|
|
99
|
+
return false;
|
|
100
|
+
if (patternVariant !== undefined) {
|
|
101
|
+
return e.pattern_variant === patternVariant;
|
|
102
|
+
}
|
|
103
|
+
return true;
|
|
104
|
+
});
|
|
87
105
|
if (hit)
|
|
88
106
|
return hit;
|
|
89
107
|
}
|
|
90
108
|
return undefined;
|
|
91
109
|
}
|
|
92
110
|
/**
|
|
93
|
-
* Resolve effective evidence for a `(module, pattern-family)` cell.
|
|
111
|
+
* Resolve effective evidence for a `(module, pattern-family[, pattern-variant])` cell.
|
|
94
112
|
*
|
|
95
|
-
* Throws if the pattern family
|
|
96
|
-
*
|
|
113
|
+
* Throws if the pattern family (or, when supplied, the specific variant)
|
|
114
|
+
* is entirely absent from the registry — that is an unrecoverable gap
|
|
115
|
+
* (the CLI must fail, not guess).
|
|
97
116
|
*/
|
|
98
|
-
export function resolveEvidence(registry, module, patternFamily) {
|
|
99
|
-
const entry = findPatternEntry(registry, patternFamily);
|
|
117
|
+
export function resolveEvidence(registry, module, patternFamily, patternVariant) {
|
|
118
|
+
const entry = findPatternEntry(registry, patternFamily, patternVariant);
|
|
100
119
|
if (!entry) {
|
|
101
|
-
|
|
120
|
+
const variantSuffix = patternVariant !== undefined ? ` (variant "${patternVariant}")` : "";
|
|
121
|
+
throw new Error(`Pattern family "${patternFamily}"${variantSuffix} is absent from verified-attrs.json. ` +
|
|
102
122
|
`The CLI cannot emit an unregistered attr. File this as a registry-gap backlog candidate.`);
|
|
103
123
|
}
|
|
104
124
|
const patternLevel = levelNumber(registry, entry.pattern_evidence_level);
|
|
@@ -113,6 +133,7 @@ export function resolveEvidence(registry, module, patternFamily) {
|
|
|
113
133
|
"verified-attrs.json (no source field)";
|
|
114
134
|
return {
|
|
115
135
|
patternFamily,
|
|
136
|
+
patternVariant: entry.pattern_variant,
|
|
116
137
|
module,
|
|
117
138
|
patternLevel,
|
|
118
139
|
patternLevelName: entry.pattern_evidence_level,
|
|
@@ -141,7 +162,10 @@ export class EvidenceGateError extends Error {
|
|
|
141
162
|
thresholdNumber;
|
|
142
163
|
thresholdName;
|
|
143
164
|
constructor(patternFamily, module, resolution, thresholdNumber, thresholdName) {
|
|
144
|
-
|
|
165
|
+
const variantTag = resolution.patternVariant !== undefined
|
|
166
|
+
? ` variant "${resolution.patternVariant}"`
|
|
167
|
+
: "";
|
|
168
|
+
super(`Evidence-gate refusal: attr family "${patternFamily}"${variantTag} on module "${module}" ` +
|
|
145
169
|
`has EFFECTIVE evidence ${resolution.effectiveLevelName} (${resolution.effectiveLevel}), ` +
|
|
146
170
|
`below the write threshold ${thresholdName} (${thresholdNumber}). ` +
|
|
147
171
|
(resolution.applicabilityMissing
|
|
@@ -158,8 +182,8 @@ export class EvidenceGateError extends Error {
|
|
|
158
182
|
this.name = "EvidenceGateError";
|
|
159
183
|
}
|
|
160
184
|
}
|
|
161
|
-
export function gateWriteAttr(registry, module, patternFamily) {
|
|
162
|
-
const resolution = resolveEvidence(registry, module, patternFamily);
|
|
185
|
+
export function gateWriteAttr(registry, module, patternFamily, patternVariant) {
|
|
186
|
+
const resolution = resolveEvidence(registry, module, patternFamily, patternVariant);
|
|
163
187
|
const thresholdNumber = writeThresholdNumber(registry);
|
|
164
188
|
if (resolution.effectiveLevel < thresholdNumber) {
|
|
165
189
|
throw new EvidenceGateError(patternFamily, module, resolution, thresholdNumber, WRITE_EMITTER_THRESHOLD_LEVEL);
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `divi/font-body` body-text group-preset emitter.
|
|
3
|
+
*
|
|
4
|
+
* Emits a byte-canonical Divi 5.5.x `type: "group"` `divi/font-body` preset
|
|
5
|
+
* targeting `divi/text` at
|
|
6
|
+
* `attrs.content.decoration.bodyFont.body.font.desktop.value.*`,
|
|
7
|
+
* gated by the verified-attrs registry. Canonical shape:
|
|
8
|
+
* `docs/verification/canonical-preset-shapes-per-module-type-2026-05-18.md`
|
|
9
|
+
* (`divi/font-body` section), cross-checked against
|
|
10
|
+
* `docs/verification/evidence/canonical-shape-dumps-2026-05-18/round-2-body-text-pattern-a.json`.
|
|
11
|
+
*
|
|
12
|
+
* Scope: Pattern A (Google Fonts) ONLY. Pattern B (local-hosted) has NO
|
|
13
|
+
* registry entry for `divi/font-body` — the heading-vs-body shape
|
|
14
|
+
* divergence question deferred per round-2 _meta. Selecting
|
|
15
|
+
* `pattern: "local"` therefore resolves to a missing-variant registry-
|
|
16
|
+
* absence error from `resolveEvidence` (the standard
|
|
17
|
+
* `findPatternEntry → undefined → throw` shape). No special-cased
|
|
18
|
+
* Pattern-B branch lives in this file — the registry IS the gate.
|
|
19
|
+
*
|
|
20
|
+
* Shape rules enforced here:
|
|
21
|
+
* - Emit-on-specification only — omitted params produce NO keys.
|
|
22
|
+
* - `attrs == styleAttrs == renderAttrs` at the route layer (the plugin's
|
|
23
|
+
* `/preset/create` route mirrors `attrs` into all three buckets to match
|
|
24
|
+
* VB save semantics); the CLI request body only carries `attrs`.
|
|
25
|
+
* - Wrapper chain: `content.decoration.bodyFont.body.font.desktop.value.*` —
|
|
26
|
+
* note the per-bucket divergence vs `divi/font` (which uses
|
|
27
|
+
* `title.decoration.font.font.desktop.value.*`). Sibling buckets do NOT
|
|
28
|
+
* share canonical keys (per `feedback_preset_map_per_module`).
|
|
29
|
+
* - Link color (`bodyFont.link.font.desktop.value.color`) is OUT OF SCOPE
|
|
30
|
+
* for this emitter — no `--link-color` option, no link emission. Needs
|
|
31
|
+
* a dedicated capture-backed follow-up before the CLI writes it.
|
|
32
|
+
* - Body-text emission on modules OTHER than `divi/text` is OUT OF SCOPE
|
|
33
|
+
* — Testimonial / Accordion-item / Slide / Blurb cells are
|
|
34
|
+
* `SCHEMA_OBSERVED` in the registry today (under the write threshold)
|
|
35
|
+
* and the gate will refuse them.
|
|
36
|
+
*/
|
|
37
|
+
import { type VerifiedAttrsRegistry } from "./registry.js";
|
|
38
|
+
export declare const TEXT_BODY_FONT_MODULE = "divi/text";
|
|
39
|
+
export declare const TEXT_BODY_FONT_GROUP_NAME = "divi/font-body";
|
|
40
|
+
export declare const TEXT_BODY_FONT_GROUP_ID = "designText";
|
|
41
|
+
export declare const TEXT_BODY_FONT_PATTERN_FAMILY = "divi/font-body";
|
|
42
|
+
/**
|
|
43
|
+
* The verified pattern variants the CLI honors for `divi/font-body`.
|
|
44
|
+
*
|
|
45
|
+
* Only Pattern A is supported. Pattern B is included in the type so the
|
|
46
|
+
* CLI surface mirrors `heading-font` (and so `--pattern local` parses
|
|
47
|
+
* cleanly enough to reach the registry-gate refusal), but its registry
|
|
48
|
+
* entry is intentionally absent — `resolveEvidence` will throw a
|
|
49
|
+
* registry-absence error when the local variant is selected. There is NO
|
|
50
|
+
* shape-level Pattern B encoding here (no weight-in-family heuristic, no
|
|
51
|
+
* `weight`-refusal branch beyond what the missing-variant gate produces).
|
|
52
|
+
*/
|
|
53
|
+
export declare const TEXT_BODY_FONT_PATTERN_VARIANTS: {
|
|
54
|
+
readonly google: "google_fonts_pattern_a";
|
|
55
|
+
readonly local: "local_hosted_pattern_b";
|
|
56
|
+
};
|
|
57
|
+
export type TextBodyFontPattern = "google" | "local";
|
|
58
|
+
export interface TextBodyFontEmitterInput {
|
|
59
|
+
/** Required display name for the preset. */
|
|
60
|
+
name: string;
|
|
61
|
+
/**
|
|
62
|
+
* Required pattern selector. There is no default. Only Pattern A is
|
|
63
|
+
* supported; passing `"local"` resolves to a registry-absence refusal
|
|
64
|
+
* (no Pattern B entry exists for `divi/font-body` today).
|
|
65
|
+
*/
|
|
66
|
+
pattern: TextBodyFontPattern;
|
|
67
|
+
/** Font family (Pattern A: plain family name, e.g. `"Inter"`). */
|
|
68
|
+
family?: string;
|
|
69
|
+
/** Font weight (numeric-string, e.g. `"400"`); emit-on-specification. */
|
|
70
|
+
weight?: string;
|
|
71
|
+
/** Font color — literal hex or bare/formed variable token. */
|
|
72
|
+
color?: string;
|
|
73
|
+
/** Font size — literal (e.g. `"16px"`) or already-formed `$variable(...)$`. */
|
|
74
|
+
size?: string;
|
|
75
|
+
/** Line height — optional, emit-on-specification only. */
|
|
76
|
+
lineHeight?: string;
|
|
77
|
+
}
|
|
78
|
+
/** The composed canonical preset entry shape sent to `/preset/create`. */
|
|
79
|
+
export interface TextBodyFontPresetEntry {
|
|
80
|
+
type: "group";
|
|
81
|
+
module_name: string;
|
|
82
|
+
group_name: string;
|
|
83
|
+
group_id: string;
|
|
84
|
+
pattern_variant: string;
|
|
85
|
+
name: string;
|
|
86
|
+
attrs: Record<string, unknown>;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Compose the canonical
|
|
90
|
+
* `attrs.content.decoration.bodyFont.body.font.desktop.value` bag from the
|
|
91
|
+
* input. Emit-on-specification: only specified sub-fields produce keys.
|
|
92
|
+
*/
|
|
93
|
+
export declare function composeTextBodyFontAttrs(input: TextBodyFontEmitterInput): Record<string, unknown>;
|
|
94
|
+
/**
|
|
95
|
+
* Emit a canonical `divi/font-body` body-text group preset for `divi/text`.
|
|
96
|
+
*
|
|
97
|
+
* 1. Validate input shape (name, pattern).
|
|
98
|
+
* 2. Compose `attrs.content.decoration.bodyFont.body.font.desktop.value.*`
|
|
99
|
+
* (emit-on-specification).
|
|
100
|
+
* 3. Gate the chosen `(divi/font-body, pattern_variant)` cell on
|
|
101
|
+
* `divi/text` against the verified-attrs registry — throws an Error
|
|
102
|
+
* (registry-absence) when the variant is missing (Pattern B is
|
|
103
|
+
* missing by design), or `EvidenceGateError` when effective evidence
|
|
104
|
+
* is below `VB_PRESET_STORAGE_VERIFIED`.
|
|
105
|
+
*
|
|
106
|
+
* `styleAttrs` and `renderAttrs` are intentionally NOT part of this
|
|
107
|
+
* entry: the plugin's `/preset/create` route mirrors the single `attrs`
|
|
108
|
+
* bag into all three buckets, writing `attrs == styleAttrs == renderAttrs`
|
|
109
|
+
* to match VB save semantics. The Round 2 fixture captures the post-write
|
|
110
|
+
* storage shape (note: round-2 _meta records `renderAttrs_present: false`
|
|
111
|
+
* for font-styling group presets, which is consistent with rounds 1a/1b —
|
|
112
|
+
* the mirror happens at the route layer regardless).
|
|
113
|
+
*/
|
|
114
|
+
export declare function emitTextBodyFontGroupPreset(input: TextBodyFontEmitterInput, registry?: VerifiedAttrsRegistry): TextBodyFontPresetEntry;
|
|
115
|
+
/**
|
|
116
|
+
* Build the `POST /diviops/v1/preset/create` request body from a
|
|
117
|
+
* text-body-font preset entry. Matches the body shape the
|
|
118
|
+
* `diviops_preset_create` MCP tool posts — the CLI reuses the existing
|
|
119
|
+
* route, it does not add one.
|
|
120
|
+
*
|
|
121
|
+
* `pattern_variant` is informational metadata on the in-memory entry and
|
|
122
|
+
* is NOT sent over the wire — the registry-gate decision happens
|
|
123
|
+
* client-side before the write.
|
|
124
|
+
*/
|
|
125
|
+
export declare function buildTextBodyFontPresetCreateBody(entry: TextBodyFontPresetEntry, opts?: {
|
|
126
|
+
dry_run?: boolean;
|
|
127
|
+
}): Record<string, unknown>;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `divi/font-body` body-text group-preset emitter.
|
|
3
|
+
*
|
|
4
|
+
* Emits a byte-canonical Divi 5.5.x `type: "group"` `divi/font-body` preset
|
|
5
|
+
* targeting `divi/text` at
|
|
6
|
+
* `attrs.content.decoration.bodyFont.body.font.desktop.value.*`,
|
|
7
|
+
* gated by the verified-attrs registry. Canonical shape:
|
|
8
|
+
* `docs/verification/canonical-preset-shapes-per-module-type-2026-05-18.md`
|
|
9
|
+
* (`divi/font-body` section), cross-checked against
|
|
10
|
+
* `docs/verification/evidence/canonical-shape-dumps-2026-05-18/round-2-body-text-pattern-a.json`.
|
|
11
|
+
*
|
|
12
|
+
* Scope: Pattern A (Google Fonts) ONLY. Pattern B (local-hosted) has NO
|
|
13
|
+
* registry entry for `divi/font-body` — the heading-vs-body shape
|
|
14
|
+
* divergence question deferred per round-2 _meta. Selecting
|
|
15
|
+
* `pattern: "local"` therefore resolves to a missing-variant registry-
|
|
16
|
+
* absence error from `resolveEvidence` (the standard
|
|
17
|
+
* `findPatternEntry → undefined → throw` shape). No special-cased
|
|
18
|
+
* Pattern-B branch lives in this file — the registry IS the gate.
|
|
19
|
+
*
|
|
20
|
+
* Shape rules enforced here:
|
|
21
|
+
* - Emit-on-specification only — omitted params produce NO keys.
|
|
22
|
+
* - `attrs == styleAttrs == renderAttrs` at the route layer (the plugin's
|
|
23
|
+
* `/preset/create` route mirrors `attrs` into all three buckets to match
|
|
24
|
+
* VB save semantics); the CLI request body only carries `attrs`.
|
|
25
|
+
* - Wrapper chain: `content.decoration.bodyFont.body.font.desktop.value.*` —
|
|
26
|
+
* note the per-bucket divergence vs `divi/font` (which uses
|
|
27
|
+
* `title.decoration.font.font.desktop.value.*`). Sibling buckets do NOT
|
|
28
|
+
* share canonical keys (per `feedback_preset_map_per_module`).
|
|
29
|
+
* - Link color (`bodyFont.link.font.desktop.value.color`) is OUT OF SCOPE
|
|
30
|
+
* for this emitter — no `--link-color` option, no link emission. Needs
|
|
31
|
+
* a dedicated capture-backed follow-up before the CLI writes it.
|
|
32
|
+
* - Body-text emission on modules OTHER than `divi/text` is OUT OF SCOPE
|
|
33
|
+
* — Testimonial / Accordion-item / Slide / Blurb cells are
|
|
34
|
+
* `SCHEMA_OBSERVED` in the registry today (under the write threshold)
|
|
35
|
+
* and the gate will refuse them.
|
|
36
|
+
*/
|
|
37
|
+
import { loadRegistry, gateWriteAttr, } from "./registry.js";
|
|
38
|
+
import { normalizeColorValue } from "./variable-token.js";
|
|
39
|
+
export const TEXT_BODY_FONT_MODULE = "divi/text";
|
|
40
|
+
export const TEXT_BODY_FONT_GROUP_NAME = "divi/font-body";
|
|
41
|
+
export const TEXT_BODY_FONT_GROUP_ID = "designText";
|
|
42
|
+
export const TEXT_BODY_FONT_PATTERN_FAMILY = "divi/font-body";
|
|
43
|
+
/**
|
|
44
|
+
* The verified pattern variants the CLI honors for `divi/font-body`.
|
|
45
|
+
*
|
|
46
|
+
* Only Pattern A is supported. Pattern B is included in the type so the
|
|
47
|
+
* CLI surface mirrors `heading-font` (and so `--pattern local` parses
|
|
48
|
+
* cleanly enough to reach the registry-gate refusal), but its registry
|
|
49
|
+
* entry is intentionally absent — `resolveEvidence` will throw a
|
|
50
|
+
* registry-absence error when the local variant is selected. There is NO
|
|
51
|
+
* shape-level Pattern B encoding here (no weight-in-family heuristic, no
|
|
52
|
+
* `weight`-refusal branch beyond what the missing-variant gate produces).
|
|
53
|
+
*/
|
|
54
|
+
export const TEXT_BODY_FONT_PATTERN_VARIANTS = {
|
|
55
|
+
google: "google_fonts_pattern_a",
|
|
56
|
+
local: "local_hosted_pattern_b",
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Compose the canonical
|
|
60
|
+
* `attrs.content.decoration.bodyFont.body.font.desktop.value` bag from the
|
|
61
|
+
* input. Emit-on-specification: only specified sub-fields produce keys.
|
|
62
|
+
*/
|
|
63
|
+
export function composeTextBodyFontAttrs(input) {
|
|
64
|
+
const value = {};
|
|
65
|
+
if (input.family !== undefined)
|
|
66
|
+
value.family = input.family;
|
|
67
|
+
if (input.weight !== undefined)
|
|
68
|
+
value.weight = input.weight;
|
|
69
|
+
if (input.color !== undefined)
|
|
70
|
+
value.color = normalizeColorValue(input.color);
|
|
71
|
+
if (input.size !== undefined)
|
|
72
|
+
value.size = input.size;
|
|
73
|
+
if (input.lineHeight !== undefined)
|
|
74
|
+
value.lineHeight = input.lineHeight;
|
|
75
|
+
return {
|
|
76
|
+
content: {
|
|
77
|
+
decoration: {
|
|
78
|
+
bodyFont: {
|
|
79
|
+
body: {
|
|
80
|
+
font: {
|
|
81
|
+
desktop: {
|
|
82
|
+
value,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Emit a canonical `divi/font-body` body-text group preset for `divi/text`.
|
|
93
|
+
*
|
|
94
|
+
* 1. Validate input shape (name, pattern).
|
|
95
|
+
* 2. Compose `attrs.content.decoration.bodyFont.body.font.desktop.value.*`
|
|
96
|
+
* (emit-on-specification).
|
|
97
|
+
* 3. Gate the chosen `(divi/font-body, pattern_variant)` cell on
|
|
98
|
+
* `divi/text` against the verified-attrs registry — throws an Error
|
|
99
|
+
* (registry-absence) when the variant is missing (Pattern B is
|
|
100
|
+
* missing by design), or `EvidenceGateError` when effective evidence
|
|
101
|
+
* is below `VB_PRESET_STORAGE_VERIFIED`.
|
|
102
|
+
*
|
|
103
|
+
* `styleAttrs` and `renderAttrs` are intentionally NOT part of this
|
|
104
|
+
* entry: the plugin's `/preset/create` route mirrors the single `attrs`
|
|
105
|
+
* bag into all three buckets, writing `attrs == styleAttrs == renderAttrs`
|
|
106
|
+
* to match VB save semantics. The Round 2 fixture captures the post-write
|
|
107
|
+
* storage shape (note: round-2 _meta records `renderAttrs_present: false`
|
|
108
|
+
* for font-styling group presets, which is consistent with rounds 1a/1b —
|
|
109
|
+
* the mirror happens at the route layer regardless).
|
|
110
|
+
*/
|
|
111
|
+
export function emitTextBodyFontGroupPreset(input, registry = loadRegistry()) {
|
|
112
|
+
if (!input.name || typeof input.name !== "string") {
|
|
113
|
+
throw new Error("text-body-font emitter requires a non-empty `name`.");
|
|
114
|
+
}
|
|
115
|
+
if (input.pattern !== "google" && input.pattern !== "local") {
|
|
116
|
+
throw new Error(`text-body-font emitter requires \`pattern\` to be "google" or "local"; got ${JSON.stringify(input.pattern)}. ` +
|
|
117
|
+
`There is no default — Pattern A (google) is the only registry-verified variant for ` +
|
|
118
|
+
`\`divi/font-body\` today; Pattern B (local) resolves to a registry-absence refusal.`);
|
|
119
|
+
}
|
|
120
|
+
const attrs = composeTextBodyFontAttrs(input);
|
|
121
|
+
// Sanity check: at least one styling field was specified. An empty
|
|
122
|
+
// value bag is a usage error — there is nothing to write.
|
|
123
|
+
const value = attrs.content.decoration.bodyFont.body.font.desktop
|
|
124
|
+
.value;
|
|
125
|
+
if (!value || Object.keys(value).length === 0) {
|
|
126
|
+
throw new Error("text-body-font emitter produced an empty preset — pass at least one of " +
|
|
127
|
+
"family, weight, color, size, or lineHeight.");
|
|
128
|
+
}
|
|
129
|
+
// Registry gate: variant-aware. Pattern A evidence must NOT vouch for
|
|
130
|
+
// Pattern B and vice versa — gateWriteAttr resolves by both family AND
|
|
131
|
+
// variant, so a missing/under-verified variant entry throws here.
|
|
132
|
+
// Pattern B has NO registry entry for `divi/font-body`, so
|
|
133
|
+
// `--pattern local` lands on `resolveEvidence`'s "absent from
|
|
134
|
+
// verified-attrs.json" throw natively — no special-cased branch needed.
|
|
135
|
+
const patternVariant = TEXT_BODY_FONT_PATTERN_VARIANTS[input.pattern];
|
|
136
|
+
gateWriteAttr(registry, TEXT_BODY_FONT_MODULE, TEXT_BODY_FONT_PATTERN_FAMILY, patternVariant);
|
|
137
|
+
return {
|
|
138
|
+
type: "group",
|
|
139
|
+
module_name: TEXT_BODY_FONT_MODULE,
|
|
140
|
+
group_name: TEXT_BODY_FONT_GROUP_NAME,
|
|
141
|
+
group_id: TEXT_BODY_FONT_GROUP_ID,
|
|
142
|
+
pattern_variant: patternVariant,
|
|
143
|
+
name: input.name,
|
|
144
|
+
attrs,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Build the `POST /diviops/v1/preset/create` request body from a
|
|
149
|
+
* text-body-font preset entry. Matches the body shape the
|
|
150
|
+
* `diviops_preset_create` MCP tool posts — the CLI reuses the existing
|
|
151
|
+
* route, it does not add one.
|
|
152
|
+
*
|
|
153
|
+
* `pattern_variant` is informational metadata on the in-memory entry and
|
|
154
|
+
* is NOT sent over the wire — the registry-gate decision happens
|
|
155
|
+
* client-side before the write.
|
|
156
|
+
*/
|
|
157
|
+
export function buildTextBodyFontPresetCreateBody(entry, opts = {}) {
|
|
158
|
+
const body = {
|
|
159
|
+
module_name: entry.module_name,
|
|
160
|
+
name: entry.name,
|
|
161
|
+
attrs: entry.attrs,
|
|
162
|
+
type: entry.type,
|
|
163
|
+
group_name: entry.group_name,
|
|
164
|
+
group_id: entry.group_id,
|
|
165
|
+
};
|
|
166
|
+
if (opts.dry_run)
|
|
167
|
+
body.dry_run = true;
|
|
168
|
+
return body;
|
|
169
|
+
}
|
|
@@ -18,6 +18,8 @@ import { WPClient } from "../wp-client.js";
|
|
|
18
18
|
import type { HandshakeResult } from "../compatibility.js";
|
|
19
19
|
import type { DiviopsResponse } from "../envelope.js";
|
|
20
20
|
import type { ButtonPresetEntry } from "./button-emitter.js";
|
|
21
|
+
import type { HeadingFontPresetEntry } from "./heading-font-emitter.js";
|
|
22
|
+
import type { TextBodyFontPresetEntry } from "./text-body-font-emitter.js";
|
|
21
23
|
/** The plugin capability the storage-path capability contract ships. */
|
|
22
24
|
export declare const STORAGE_CAPABILITY = "storage_multipath_probe_v1";
|
|
23
25
|
/** The REST route the CLI posts to — same route `diviops_preset_create` uses. */
|
|
@@ -57,3 +59,32 @@ export declare function applyButtonPreset(client: PresetWriteClient, entry: Butt
|
|
|
57
59
|
serverVersion: string;
|
|
58
60
|
dry_run?: boolean;
|
|
59
61
|
}): Promise<DiviopsResponse<unknown>>;
|
|
62
|
+
/**
|
|
63
|
+
* Apply a `divi/font` heading group preset: capability-gate, then POST
|
|
64
|
+
* to `/preset/create`. Mirrors `applyButtonPreset`'s sequence — the
|
|
65
|
+
* capability check runs BEFORE the write, and the write reuses the
|
|
66
|
+
* existing storage-routed route (no plugin route is added).
|
|
67
|
+
*
|
|
68
|
+
* The `pattern_variant` metadata is intentionally NOT in the wire body —
|
|
69
|
+
* variant selection is a client-side registry-gate decision and the
|
|
70
|
+
* server route accepts only the standard preset-create fields.
|
|
71
|
+
*/
|
|
72
|
+
export declare function applyHeadingFontPreset(client: PresetWriteClient, entry: HeadingFontPresetEntry, opts: {
|
|
73
|
+
serverVersion: string;
|
|
74
|
+
dry_run?: boolean;
|
|
75
|
+
}): Promise<DiviopsResponse<unknown>>;
|
|
76
|
+
/**
|
|
77
|
+
* Apply a `divi/font-body` body-text group preset for `divi/text`:
|
|
78
|
+
* capability-gate, then POST to `/preset/create`. Mirrors
|
|
79
|
+
* `applyHeadingFontPreset`'s sequence — the capability check runs BEFORE
|
|
80
|
+
* the write, and the write reuses the existing storage-routed route (no
|
|
81
|
+
* plugin route is added).
|
|
82
|
+
*
|
|
83
|
+
* The `pattern_variant` metadata is intentionally NOT in the wire body —
|
|
84
|
+
* variant selection is a client-side registry-gate decision and the
|
|
85
|
+
* server route accepts only the standard preset-create fields.
|
|
86
|
+
*/
|
|
87
|
+
export declare function applyTextBodyFontPreset(client: PresetWriteClient, entry: TextBodyFontPresetEntry, opts: {
|
|
88
|
+
serverVersion: string;
|
|
89
|
+
dry_run?: boolean;
|
|
90
|
+
}): Promise<DiviopsResponse<unknown>>;
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { WPClient } from "../wp-client.js";
|
|
18
18
|
import { buildPresetCreateBody } from "./button-emitter.js";
|
|
19
|
+
import { buildHeadingFontPresetCreateBody } from "./heading-font-emitter.js";
|
|
20
|
+
import { buildTextBodyFontPresetCreateBody } from "./text-body-font-emitter.js";
|
|
19
21
|
/** The plugin capability the storage-path capability contract ships. */
|
|
20
22
|
export const STORAGE_CAPABILITY = "storage_multipath_probe_v1";
|
|
21
23
|
/** The REST route the CLI posts to — same route `diviops_preset_create` uses. */
|
|
@@ -87,3 +89,44 @@ export async function applyButtonPreset(client, entry, opts) {
|
|
|
87
89
|
body,
|
|
88
90
|
});
|
|
89
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Apply a `divi/font` heading group preset: capability-gate, then POST
|
|
94
|
+
* to `/preset/create`. Mirrors `applyButtonPreset`'s sequence — the
|
|
95
|
+
* capability check runs BEFORE the write, and the write reuses the
|
|
96
|
+
* existing storage-routed route (no plugin route is added).
|
|
97
|
+
*
|
|
98
|
+
* The `pattern_variant` metadata is intentionally NOT in the wire body —
|
|
99
|
+
* variant selection is a client-side registry-gate decision and the
|
|
100
|
+
* server route accepts only the standard preset-create fields.
|
|
101
|
+
*/
|
|
102
|
+
export async function applyHeadingFontPreset(client, entry, opts) {
|
|
103
|
+
await assertStorageCapability(client, opts.serverVersion);
|
|
104
|
+
const body = buildHeadingFontPresetCreateBody(entry, {
|
|
105
|
+
dry_run: opts.dry_run,
|
|
106
|
+
});
|
|
107
|
+
return client.requestEnveloped(PRESET_CREATE_ROUTE, {
|
|
108
|
+
method: "POST",
|
|
109
|
+
body,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Apply a `divi/font-body` body-text group preset for `divi/text`:
|
|
114
|
+
* capability-gate, then POST to `/preset/create`. Mirrors
|
|
115
|
+
* `applyHeadingFontPreset`'s sequence — the capability check runs BEFORE
|
|
116
|
+
* the write, and the write reuses the existing storage-routed route (no
|
|
117
|
+
* plugin route is added).
|
|
118
|
+
*
|
|
119
|
+
* The `pattern_variant` metadata is intentionally NOT in the wire body —
|
|
120
|
+
* variant selection is a client-side registry-gate decision and the
|
|
121
|
+
* server route accepts only the standard preset-create fields.
|
|
122
|
+
*/
|
|
123
|
+
export async function applyTextBodyFontPreset(client, entry, opts) {
|
|
124
|
+
await assertStorageCapability(client, opts.serverVersion);
|
|
125
|
+
const body = buildTextBodyFontPresetCreateBody(entry, {
|
|
126
|
+
dry_run: opts.dry_run,
|
|
127
|
+
});
|
|
128
|
+
return client.requestEnveloped(PRESET_CREATE_ROUTE, {
|
|
129
|
+
method: "POST",
|
|
130
|
+
body,
|
|
131
|
+
});
|
|
132
|
+
}
|