@diviops/mcp-server 1.5.14 → 1.5.16
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 +10 -1
- package/data/verified-attrs-backlog.json +5 -5
- package/data/verified-attrs.json +8 -8
- package/dist/compatibility.d.ts +25 -0
- package/dist/index.js +349 -4
- package/dist/preset-cli/button-emitter.d.ts +1 -1
- package/dist/preset-cli/button-emitter.js +1 -1
- package/dist/preset-cli/cli.d.ts +4 -3
- package/dist/preset-cli/cli.js +11 -10
- package/dist/preset-cli/heading-font-emitter.d.ts +1 -1
- package/dist/preset-cli/heading-font-emitter.js +3 -3
- package/dist/preset-cli/spacing-emitter.d.ts +13 -13
- package/dist/preset-cli/spacing-emitter.js +23 -23
- package/dist/preset-cli/write-path.d.ts +3 -3
- package/dist/preset-cli/write-path.js +3 -3
- package/dist/wp-cli-fs-validator.d.ts +1 -1
- package/dist/wp-cli-fs-validator.js +1 -1
- package/dist/wp-cli.js +1 -2
- package/dist/wp-client.js +17 -0
- package/package.json +3 -2
- package/dist/preset-cli/__tests__/button-emitter.test.d.ts +0 -8
- package/dist/preset-cli/__tests__/button-emitter.test.js +0 -188
- package/dist/preset-cli/__tests__/cli.test.d.ts +0 -9
- package/dist/preset-cli/__tests__/cli.test.js +0 -791
- package/dist/preset-cli/__tests__/heading-font-emitter.test.d.ts +0 -12
- package/dist/preset-cli/__tests__/heading-font-emitter.test.js +0 -249
- package/dist/preset-cli/__tests__/preset-create-unchanged.test.d.ts +0 -13
- package/dist/preset-cli/__tests__/preset-create-unchanged.test.js +0 -64
- package/dist/preset-cli/__tests__/registry.test.d.ts +0 -5
- package/dist/preset-cli/__tests__/registry.test.js +0 -175
- package/dist/preset-cli/__tests__/spacing-emitter.test.d.ts +0 -20
- package/dist/preset-cli/__tests__/spacing-emitter.test.js +0 -409
- package/dist/preset-cli/__tests__/text-body-font-emitter.test.d.ts +0 -14
- package/dist/preset-cli/__tests__/text-body-font-emitter.test.js +0 -191
- package/dist/preset-cli/__tests__/write-path.test.d.ts +0 -8
- package/dist/preset-cli/__tests__/write-path.test.js +0 -287
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Apply-mode coverage — mocked only (no live substrate write, per #725 AC #8).
|
|
3
|
-
*
|
|
4
|
-
* Asserts: the capability gate (storage_multipath_probe_v1 present → proceed,
|
|
5
|
-
* absent → fail fast), the POST route + request body, and credential
|
|
6
|
-
* handling. The HTTP client is a stub; nothing touches the network.
|
|
7
|
-
*/
|
|
8
|
-
import { test } from "node:test";
|
|
9
|
-
import assert from "node:assert/strict";
|
|
10
|
-
import { applyButtonPreset, applyHeadingFontPreset, applyTextBodyFontPreset, applySpacingPreset, assertStorageCapability, buildClientFromEnv, CapabilityMissingError, CredentialsMissingError, STORAGE_CAPABILITY, PRESET_CREATE_ROUTE, } from "../write-path.js";
|
|
11
|
-
import { emitButtonGroupPreset } from "../button-emitter.js";
|
|
12
|
-
import { emitHeadingFontGroupPreset } from "../heading-font-emitter.js";
|
|
13
|
-
import { emitTextBodyFontGroupPreset } from "../text-body-font-emitter.js";
|
|
14
|
-
import { emitSpacingGroupPreset } from "../spacing-emitter.js";
|
|
15
|
-
import { loadRegistry } from "../registry.js";
|
|
16
|
-
const registry = loadRegistry();
|
|
17
|
-
function handshake(capabilities) {
|
|
18
|
-
return {
|
|
19
|
-
compatible: true,
|
|
20
|
-
plugin_version: "1.4.9",
|
|
21
|
-
min_server: "1.5.0",
|
|
22
|
-
divi: { active: true, version: "5.5.2" },
|
|
23
|
-
capabilities,
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
const TEST_SERVER_VERSION = "1.5.9";
|
|
27
|
-
/** A mock client recording the calls it receives. */
|
|
28
|
-
function mockClient(opts) {
|
|
29
|
-
const calls = [];
|
|
30
|
-
const handshakeVersions = [];
|
|
31
|
-
return {
|
|
32
|
-
calls,
|
|
33
|
-
handshakeVersions,
|
|
34
|
-
async handshake(serverVersion) {
|
|
35
|
-
handshakeVersions.push(serverVersion);
|
|
36
|
-
const hs = handshake(opts.capabilities);
|
|
37
|
-
if (opts.pluginVersion)
|
|
38
|
-
hs.plugin_version = opts.pluginVersion;
|
|
39
|
-
return hs;
|
|
40
|
-
},
|
|
41
|
-
async requestEnveloped(endpoint, options) {
|
|
42
|
-
calls.push({ endpoint, options });
|
|
43
|
-
return { ok: true, data: { preset_id: "mocked123" } };
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
test("assertStorageCapability proceeds when the capability is present", async () => {
|
|
48
|
-
const client = mockClient({ capabilities: { [STORAGE_CAPABILITY]: true } });
|
|
49
|
-
const hs = await assertStorageCapability(client, TEST_SERVER_VERSION);
|
|
50
|
-
assert.equal(hs.capabilities[STORAGE_CAPABILITY], true);
|
|
51
|
-
assert.deepEqual(client.handshakeVersions, [TEST_SERVER_VERSION], "the server version reaches handshake()");
|
|
52
|
-
assert.ok(client.handshakeVersions.every((v) => typeof v === "string" && v.length > 0), "handshake() never receives an undefined/empty server version");
|
|
53
|
-
});
|
|
54
|
-
test("assertStorageCapability fails fast when the capability is absent", async () => {
|
|
55
|
-
const client = mockClient({ capabilities: {}, pluginVersion: "1.4.8" });
|
|
56
|
-
await assert.rejects(() => assertStorageCapability(client, TEST_SERVER_VERSION), (err) => {
|
|
57
|
-
assert.ok(err instanceof CapabilityMissingError);
|
|
58
|
-
assert.equal(err.capability, STORAGE_CAPABILITY);
|
|
59
|
-
assert.match(err.message, /1\.4\.8/);
|
|
60
|
-
return true;
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
test("applyButtonPreset gates capability BEFORE issuing the write", async () => {
|
|
64
|
-
const client = mockClient({ capabilities: {} });
|
|
65
|
-
const entry = emitButtonGroupPreset({ name: "Primary", bg_color: "#111" }, registry);
|
|
66
|
-
await assert.rejects(() => applyButtonPreset(client, entry, { serverVersion: TEST_SERVER_VERSION }), CapabilityMissingError);
|
|
67
|
-
assert.equal(client.calls.length, 0, "no write issued when capability is absent");
|
|
68
|
-
});
|
|
69
|
-
test("applyButtonPreset posts to /preset/create with the canonical body", async () => {
|
|
70
|
-
const client = mockClient({ capabilities: { [STORAGE_CAPABILITY]: true } });
|
|
71
|
-
const entry = emitButtonGroupPreset({
|
|
72
|
-
name: "Primary",
|
|
73
|
-
bg_color: "gcid-primary-color",
|
|
74
|
-
bg_color_hover: "gcid-secondary-color",
|
|
75
|
-
radius: { topLeft: "8px", topRight: "8px", bottomLeft: "8px", bottomRight: "8px" },
|
|
76
|
-
font: { family: "Inter", weight: "600", color: "gcid-body-color" },
|
|
77
|
-
}, registry);
|
|
78
|
-
const result = await applyButtonPreset(client, entry, {
|
|
79
|
-
serverVersion: TEST_SERVER_VERSION,
|
|
80
|
-
});
|
|
81
|
-
assert.equal(client.handshakeVersions[0], TEST_SERVER_VERSION, "applyButtonPreset threads a non-empty server version into handshake()");
|
|
82
|
-
assert.ok((client.handshakeVersions[0] ?? "").length > 0, "handshake() server version is never empty in apply mode");
|
|
83
|
-
assert.equal(client.calls.length, 1, "exactly one write");
|
|
84
|
-
const call = client.calls[0];
|
|
85
|
-
assert.equal(call.endpoint, PRESET_CREATE_ROUTE, "posts to /preset/create");
|
|
86
|
-
const options = call.options;
|
|
87
|
-
assert.equal(options.method, "POST");
|
|
88
|
-
assert.equal(options.body.type, "group");
|
|
89
|
-
assert.equal(options.body.module_name, "divi/button");
|
|
90
|
-
assert.equal(options.body.group_name, "divi/button");
|
|
91
|
-
assert.equal(options.body.group_id, "button");
|
|
92
|
-
assert.equal(options.body.name, "Primary");
|
|
93
|
-
assert.deepEqual(options.body.attrs, entry.attrs);
|
|
94
|
-
assert.equal("dry_run" in options.body, false, "apply mode does not set dry_run");
|
|
95
|
-
assert.deepEqual(result, { ok: true, data: { preset_id: "mocked123" } });
|
|
96
|
-
});
|
|
97
|
-
test("applyButtonPreset threads dry_run into the body when requested", async () => {
|
|
98
|
-
const client = mockClient({ capabilities: { [STORAGE_CAPABILITY]: true } });
|
|
99
|
-
const entry = emitButtonGroupPreset({ name: "P", bg_color: "#111" }, registry);
|
|
100
|
-
await applyButtonPreset(client, entry, {
|
|
101
|
-
serverVersion: TEST_SERVER_VERSION,
|
|
102
|
-
dry_run: true,
|
|
103
|
-
});
|
|
104
|
-
const options = client.calls[0].options;
|
|
105
|
-
assert.equal(options.body.dry_run, true);
|
|
106
|
-
});
|
|
107
|
-
// ------------------------------------------------------------------
|
|
108
|
-
// heading-font apply-mode — mocked only.
|
|
109
|
-
// Mirrors the button apply-mode coverage: capability gate first, then a
|
|
110
|
-
// single POST to /preset/create with the canonical body. pattern_variant
|
|
111
|
-
// is in-memory only and must NOT appear in the wire body.
|
|
112
|
-
// ------------------------------------------------------------------
|
|
113
|
-
test("applyHeadingFontPreset gates capability BEFORE issuing the write", async () => {
|
|
114
|
-
const client = mockClient({ capabilities: {} });
|
|
115
|
-
const entry = emitHeadingFontGroupPreset({ name: "H1", pattern: "google", family: "Inter", weight: "700" }, registry);
|
|
116
|
-
await assert.rejects(() => applyHeadingFontPreset(client, entry, {
|
|
117
|
-
serverVersion: TEST_SERVER_VERSION,
|
|
118
|
-
}), CapabilityMissingError);
|
|
119
|
-
assert.equal(client.calls.length, 0, "no write issued when capability is absent");
|
|
120
|
-
});
|
|
121
|
-
test("applyHeadingFontPreset posts to /preset/create with the canonical body", async () => {
|
|
122
|
-
const client = mockClient({ capabilities: { [STORAGE_CAPABILITY]: true } });
|
|
123
|
-
const entry = emitHeadingFontGroupPreset({
|
|
124
|
-
name: "H1",
|
|
125
|
-
pattern: "google",
|
|
126
|
-
family: "Inter",
|
|
127
|
-
weight: "700",
|
|
128
|
-
color: "gcid-heading-color",
|
|
129
|
-
size: "48px",
|
|
130
|
-
}, registry);
|
|
131
|
-
const result = await applyHeadingFontPreset(client, entry, {
|
|
132
|
-
serverVersion: TEST_SERVER_VERSION,
|
|
133
|
-
});
|
|
134
|
-
assert.equal(client.handshakeVersions[0], TEST_SERVER_VERSION);
|
|
135
|
-
assert.equal(client.calls.length, 1);
|
|
136
|
-
const call = client.calls[0];
|
|
137
|
-
assert.equal(call.endpoint, PRESET_CREATE_ROUTE);
|
|
138
|
-
const options = call.options;
|
|
139
|
-
assert.equal(options.method, "POST");
|
|
140
|
-
assert.equal(options.body.type, "group");
|
|
141
|
-
assert.equal(options.body.module_name, "divi/heading");
|
|
142
|
-
assert.equal(options.body.group_name, "divi/font");
|
|
143
|
-
assert.equal(options.body.group_id, "designTitleText");
|
|
144
|
-
assert.equal(options.body.name, "H1");
|
|
145
|
-
assert.deepEqual(options.body.attrs, entry.attrs);
|
|
146
|
-
assert.equal("pattern_variant" in options.body, false, "pattern_variant is client-side gating metadata; it must not be on the wire");
|
|
147
|
-
assert.equal("dry_run" in options.body, false);
|
|
148
|
-
assert.deepEqual(result, { ok: true, data: { preset_id: "mocked123" } });
|
|
149
|
-
});
|
|
150
|
-
test("applyHeadingFontPreset threads dry_run into the body when requested", async () => {
|
|
151
|
-
const client = mockClient({ capabilities: { [STORAGE_CAPABILITY]: true } });
|
|
152
|
-
const entry = emitHeadingFontGroupPreset({ name: "H1", pattern: "local", family: "Sora 700" }, registry);
|
|
153
|
-
await applyHeadingFontPreset(client, entry, {
|
|
154
|
-
serverVersion: TEST_SERVER_VERSION,
|
|
155
|
-
dry_run: true,
|
|
156
|
-
});
|
|
157
|
-
const options = client.calls[0].options;
|
|
158
|
-
assert.equal(options.body.dry_run, true);
|
|
159
|
-
});
|
|
160
|
-
// ------------------------------------------------------------------
|
|
161
|
-
// text-body-font apply-mode — mocked only (Track 6).
|
|
162
|
-
// Mirrors the heading-font apply-mode coverage. pattern_variant is
|
|
163
|
-
// in-memory metadata and must NOT appear in the wire body. Pattern B is
|
|
164
|
-
// refused at the emitter level (registry-absence) — apply-mode is never
|
|
165
|
-
// reached for `--pattern local`, so it has no mocked test here.
|
|
166
|
-
// ------------------------------------------------------------------
|
|
167
|
-
test("applyTextBodyFontPreset gates capability BEFORE issuing the write", async () => {
|
|
168
|
-
const client = mockClient({ capabilities: {} });
|
|
169
|
-
const entry = emitTextBodyFontGroupPreset({ name: "Body", pattern: "google", family: "Inter", weight: "400" }, registry);
|
|
170
|
-
await assert.rejects(() => applyTextBodyFontPreset(client, entry, {
|
|
171
|
-
serverVersion: TEST_SERVER_VERSION,
|
|
172
|
-
}), CapabilityMissingError);
|
|
173
|
-
assert.equal(client.calls.length, 0, "no write issued when capability is absent");
|
|
174
|
-
});
|
|
175
|
-
test("applyTextBodyFontPreset posts to /preset/create with the canonical body", async () => {
|
|
176
|
-
const client = mockClient({ capabilities: { [STORAGE_CAPABILITY]: true } });
|
|
177
|
-
const entry = emitTextBodyFontGroupPreset({
|
|
178
|
-
name: "Body",
|
|
179
|
-
pattern: "google",
|
|
180
|
-
family: "Inter",
|
|
181
|
-
weight: "400",
|
|
182
|
-
color: "gcid-body-color",
|
|
183
|
-
size: "16px",
|
|
184
|
-
}, registry);
|
|
185
|
-
const result = await applyTextBodyFontPreset(client, entry, {
|
|
186
|
-
serverVersion: TEST_SERVER_VERSION,
|
|
187
|
-
});
|
|
188
|
-
assert.equal(client.handshakeVersions[0], TEST_SERVER_VERSION);
|
|
189
|
-
assert.equal(client.calls.length, 1);
|
|
190
|
-
const call = client.calls[0];
|
|
191
|
-
assert.equal(call.endpoint, PRESET_CREATE_ROUTE);
|
|
192
|
-
const options = call.options;
|
|
193
|
-
assert.equal(options.method, "POST");
|
|
194
|
-
assert.equal(options.body.type, "group");
|
|
195
|
-
assert.equal(options.body.module_name, "divi/text");
|
|
196
|
-
assert.equal(options.body.group_name, "divi/font-body");
|
|
197
|
-
assert.equal(options.body.group_id, "designText");
|
|
198
|
-
assert.equal(options.body.name, "Body");
|
|
199
|
-
assert.deepEqual(options.body.attrs, entry.attrs);
|
|
200
|
-
assert.equal("pattern_variant" in options.body, false, "pattern_variant is client-side gating metadata; it must not be on the wire");
|
|
201
|
-
assert.equal("dry_run" in options.body, false);
|
|
202
|
-
assert.deepEqual(result, { ok: true, data: { preset_id: "mocked123" } });
|
|
203
|
-
});
|
|
204
|
-
test("applyTextBodyFontPreset threads dry_run into the body when requested", async () => {
|
|
205
|
-
const client = mockClient({ capabilities: { [STORAGE_CAPABILITY]: true } });
|
|
206
|
-
const entry = emitTextBodyFontGroupPreset({ name: "Body", pattern: "google", family: "Inter" }, registry);
|
|
207
|
-
await applyTextBodyFontPreset(client, entry, {
|
|
208
|
-
serverVersion: TEST_SERVER_VERSION,
|
|
209
|
-
dry_run: true,
|
|
210
|
-
});
|
|
211
|
-
const options = client.calls[0].options;
|
|
212
|
-
assert.equal(options.body.dry_run, true);
|
|
213
|
-
});
|
|
214
|
-
// ------------------------------------------------------------------
|
|
215
|
-
// applySpacingPreset — Track 7b mocked apply-mode coverage.
|
|
216
|
-
// Mirrors the text-body-font apply-mode coverage. Notably the spacing
|
|
217
|
-
// body DOES carry primary_attr_name on the wire (the section cell needs
|
|
218
|
-
// it per Track 7a load-bearing finding); the other emitters omit it.
|
|
219
|
-
// ------------------------------------------------------------------
|
|
220
|
-
test("applySpacingPreset gates capability BEFORE issuing the write", async () => {
|
|
221
|
-
const client = mockClient({ capabilities: {} });
|
|
222
|
-
const entry = emitSpacingGroupPreset({
|
|
223
|
-
name: "Sec",
|
|
224
|
-
module: "divi/section",
|
|
225
|
-
padding: { top: "40px" },
|
|
226
|
-
}, registry);
|
|
227
|
-
await assert.rejects(() => applySpacingPreset(client, entry, { serverVersion: TEST_SERVER_VERSION }), CapabilityMissingError);
|
|
228
|
-
assert.equal(client.calls.length, 0, "no write issued when capability is absent");
|
|
229
|
-
});
|
|
230
|
-
test("applySpacingPreset posts to /preset/create with the canonical body", async () => {
|
|
231
|
-
const client = mockClient({ capabilities: { [STORAGE_CAPABILITY]: true } });
|
|
232
|
-
const entry = emitSpacingGroupPreset({
|
|
233
|
-
name: "Section Rhythm",
|
|
234
|
-
module: "divi/section",
|
|
235
|
-
padding: { top: "80px", bottom: "80px" },
|
|
236
|
-
margin: { bottom: "40px" },
|
|
237
|
-
}, registry);
|
|
238
|
-
await applySpacingPreset(client, entry, {
|
|
239
|
-
serverVersion: TEST_SERVER_VERSION,
|
|
240
|
-
});
|
|
241
|
-
assert.equal(client.handshakeVersions[0], TEST_SERVER_VERSION);
|
|
242
|
-
assert.equal(client.calls.length, 1);
|
|
243
|
-
const call = client.calls[0];
|
|
244
|
-
assert.equal(call.endpoint, PRESET_CREATE_ROUTE);
|
|
245
|
-
const options = call.options;
|
|
246
|
-
assert.equal(options.method, "POST");
|
|
247
|
-
assert.equal(options.body.type, "group");
|
|
248
|
-
assert.equal(options.body.module_name, "divi/section");
|
|
249
|
-
assert.equal(options.body.group_name, "divi/spacing");
|
|
250
|
-
assert.equal(options.body.group_id, "designSpacing");
|
|
251
|
-
assert.equal(options.body.primary_attr_name, "module");
|
|
252
|
-
assert.deepEqual(options.body.attrs, entry.attrs);
|
|
253
|
-
assert.equal("styleAttrs" in options.body, false, "styleAttrs is mirrored at the route layer; CLI body carries attrs only");
|
|
254
|
-
assert.equal("renderAttrs" in options.body, false, "renderAttrs is mirrored at the route layer; CLI body carries attrs only");
|
|
255
|
-
assert.equal("dry_run" in options.body, false);
|
|
256
|
-
});
|
|
257
|
-
test("applySpacingPreset threads dry_run into the body when requested", async () => {
|
|
258
|
-
const client = mockClient({ capabilities: { [STORAGE_CAPABILITY]: true } });
|
|
259
|
-
const entry = emitSpacingGroupPreset({
|
|
260
|
-
name: "Sec",
|
|
261
|
-
module: "divi/section",
|
|
262
|
-
padding: { top: "40px" },
|
|
263
|
-
}, registry);
|
|
264
|
-
await applySpacingPreset(client, entry, {
|
|
265
|
-
serverVersion: TEST_SERVER_VERSION,
|
|
266
|
-
dry_run: true,
|
|
267
|
-
});
|
|
268
|
-
const options = client.calls[0].options;
|
|
269
|
-
assert.equal(options.body.dry_run, true);
|
|
270
|
-
});
|
|
271
|
-
test("buildClientFromEnv throws CredentialsMissingError when env vars are absent", () => {
|
|
272
|
-
assert.throws(() => buildClientFromEnv({}), (err) => {
|
|
273
|
-
assert.ok(err instanceof CredentialsMissingError);
|
|
274
|
-
assert.match(err.message, /WP_URL/);
|
|
275
|
-
assert.match(err.message, /WP_USER/);
|
|
276
|
-
assert.match(err.message, /WP_APP_PASSWORD/);
|
|
277
|
-
return true;
|
|
278
|
-
});
|
|
279
|
-
});
|
|
280
|
-
test("buildClientFromEnv succeeds with the standard env vars", () => {
|
|
281
|
-
const client = buildClientFromEnv({
|
|
282
|
-
WP_URL: "http://divi5-ai.local",
|
|
283
|
-
WP_USER: "admin",
|
|
284
|
-
WP_APP_PASSWORD: "xxxx xxxx xxxx",
|
|
285
|
-
});
|
|
286
|
-
assert.ok(client, "WPClient constructed from WP_URL/WP_USER/WP_APP_PASSWORD");
|
|
287
|
-
});
|