@diviops/mcp-server 0.2.19 → 0.2.20
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/dist/wp-client.js +66 -21
- package/package.json +1 -1
package/dist/wp-client.js
CHANGED
|
@@ -6,43 +6,88 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { MIN_PLUGIN_VERSION, compareVersions, } from './compatibility.js';
|
|
8
8
|
/**
|
|
9
|
-
* Normalize
|
|
10
|
-
* token regions only.
|
|
9
|
+
* Normalize quote-escape pathologies inside `$variable(...)$` token regions only.
|
|
11
10
|
*
|
|
12
|
-
* Divi block-attrs JSON uses `\"` (2-byte) for inner quotes
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* - `\u0022` (11 bytes literal) — the mass-corruption form (backslash
|
|
16
|
-
* itself unicode-escaped, observed in the wild on Divi 5.3.x sites)
|
|
17
|
-
* - `\\u0022` (7 bytes literal) — produced when a caller passes the
|
|
18
|
-
* 6-byte `"` form through an extra JSON-encoding layer
|
|
11
|
+
* Divi block-attrs JSON uses `\"` (2-byte: backslash + quote) for inner quotes
|
|
12
|
+
* inside variable token payloads. Three pathological forms can leak in through
|
|
13
|
+
* callers and silently break the WP block parser at write time.
|
|
19
14
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* `
|
|
23
|
-
*
|
|
15
|
+
* Over-escape (existing #395/#396 fix). Two forms produced when callers
|
|
16
|
+
* round-trip pre-existing broken bytes:
|
|
17
|
+
* - `\u005cu0022` (11 bytes literal) — the
|
|
18
|
+
* mass-corruption form (backslash itself unicode-escaped, observed in the
|
|
19
|
+
* wild on Divi 5.3.x sites)
|
|
20
|
+
* - `\\u0022` (7 bytes literal: 2 backslashes + `u0022`) — produced when
|
|
21
|
+
* a caller passes the 6-byte unicode-escape form through an extra
|
|
22
|
+
* JSON-encoding layer
|
|
23
|
+
* These cause render-only failure: the resolver can't decode the token, and
|
|
24
|
+
* attr paths like `background.color`, `spacing.margin`, `border.color`,
|
|
25
|
+
* `layout.columnGap` silently fall through to defaults (or leak literal
|
|
26
|
+
* `0022` into emitted CSS).
|
|
27
|
+
*
|
|
28
|
+
* Under-escape (#409 fix). One form produced when an agent transcribes
|
|
29
|
+
* `get_section` markup (which emits inner quotes as `"` HTML entities) and
|
|
30
|
+
* a layer in the agent → MCP → WP pipeline strips one level of escaping:
|
|
31
|
+
* - bare `"` (1 byte) — the inner quote loses its `\` prefix and prematurely
|
|
32
|
+
* terminates the OUTER block-attrs string at parse time. The WP block
|
|
33
|
+
* parser then silently drops ALL attrs from the affected module. Section
|
|
34
|
+
* appears to save (`success: true`) but renders empty / broken.
|
|
35
|
+
*
|
|
36
|
+
* We normalize defensively so any write — clean, pre-broken, or
|
|
37
|
+
* agent-transcribed — settles on canonical 2-byte `\"`.
|
|
38
|
+
*
|
|
39
|
+
* Order matters: collapse over-escapes first, then escape under-escapes. The
|
|
40
|
+
* negative lookbehind on the under-escape rule skips `\"` produced by the
|
|
41
|
+
* over-escape pass (and any already-canonical input). Idempotent.
|
|
24
42
|
*
|
|
25
43
|
* Scope is intentionally narrow: rewrites only happen inside `$variable(...)$`
|
|
26
44
|
* regions (Divi's actual resolver boundary). Bytes outside those regions —
|
|
27
45
|
* arbitrary user text, code samples, string-variable values that happen to
|
|
28
|
-
* contain `\u0022` — are left untouched.
|
|
46
|
+
* contain `\u005cu0022`, `\\u0022`, or bare `"` — are left untouched.
|
|
29
47
|
*/
|
|
30
48
|
function normalizeQuoteEscapes(s) {
|
|
31
|
-
return s.replace(/\$variable\([^$]*?\)\$/g, (token) =>
|
|
49
|
+
return s.replace(/\$variable\([^$]*?\)\$/g, (token) => {
|
|
50
|
+
// Pass 1: collapse over-escaped forms (#395/#396) to canonical \"
|
|
51
|
+
let normalized = token.replace(/(?:\\u005cu0022|\\\\u0022)/g, '\\"');
|
|
52
|
+
// Pass 2: escape any bare " (#409) to canonical \" — negative lookbehind
|
|
53
|
+
// skips properly-escaped quotes produced by Pass 1 or already canonical.
|
|
54
|
+
normalized = normalized.replace(/(?<!\\)"/g, '\\"');
|
|
55
|
+
return normalized;
|
|
56
|
+
});
|
|
32
57
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Body keys whose values (and descendants) carry Divi block markup or block
|
|
60
|
+
* attribute trees, where `$variable(...)$` token-region normalization is the
|
|
61
|
+
* intended behavior. Strings reachable only through other top-level keys
|
|
62
|
+
* — variable values, labels, match-text predicates, descriptions, etc.
|
|
63
|
+
* — are passed through verbatim so a literal `$variable({"x":"y"})$`
|
|
64
|
+
* docs example in a `string_variable_value` is preserved (#409 review:
|
|
65
|
+
* Codex-flagged regression — without this scoping, the bare-quote pass would
|
|
66
|
+
* silently rewrite literal token-shaped substrings in user-prose fields).
|
|
67
|
+
*/
|
|
68
|
+
const BLOCK_CONTENT_KEYS = new Set([
|
|
69
|
+
'content', // update_page_content, render_preview, validate_blocks,
|
|
70
|
+
// append_section, replace_section, update_tb_layout,
|
|
71
|
+
// save_to_library, create_page
|
|
72
|
+
'attrs', // update_module — attr values embedded in block JSON
|
|
73
|
+
'header_content', // create_tb_template
|
|
74
|
+
'footer_content', // create_tb_template
|
|
75
|
+
]);
|
|
76
|
+
function normalizeBody(value, withinBlockTree = false) {
|
|
77
|
+
if (typeof value === 'string') {
|
|
78
|
+
return withinBlockTree ? normalizeQuoteEscapes(value) : value;
|
|
79
|
+
}
|
|
36
80
|
if (Array.isArray(value))
|
|
37
|
-
return value.map(normalizeBody);
|
|
81
|
+
return value.map((v) => normalizeBody(v, withinBlockTree));
|
|
38
82
|
// Restrict recursion to plain objects so Date / RegExp / class instances
|
|
39
83
|
// with custom `toJSON` keep their canonical serialization.
|
|
40
84
|
if (value &&
|
|
41
85
|
typeof value === 'object' &&
|
|
42
86
|
Object.getPrototypeOf(value) === Object.prototype) {
|
|
43
87
|
const out = {};
|
|
44
|
-
for (const [k, v] of Object.entries(value))
|
|
45
|
-
out[k] = normalizeBody(v);
|
|
88
|
+
for (const [k, v] of Object.entries(value)) {
|
|
89
|
+
out[k] = normalizeBody(v, withinBlockTree || BLOCK_CONTENT_KEYS.has(k));
|
|
90
|
+
}
|
|
46
91
|
return out;
|
|
47
92
|
}
|
|
48
93
|
return value;
|