@respira/wordpress-mcp-server 8.3.21 → 8.3.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/__tests__/303ad1de-fse-operation-schema.test.d.ts +2 -0
  3. package/dist/__tests__/303ad1de-fse-operation-schema.test.d.ts.map +1 -0
  4. package/dist/__tests__/303ad1de-fse-operation-schema.test.js +98 -0
  5. package/dist/__tests__/303ad1de-fse-operation-schema.test.js.map +1 -0
  6. package/dist/__tests__/953dde4e-verb-rewriting-host-method-override.test.d.ts +29 -0
  7. package/dist/__tests__/953dde4e-verb-rewriting-host-method-override.test.d.ts.map +1 -0
  8. package/dist/__tests__/953dde4e-verb-rewriting-host-method-override.test.js +305 -0
  9. package/dist/__tests__/953dde4e-verb-rewriting-host-method-override.test.js.map +1 -0
  10. package/dist/__tests__/9dee06c4-patch-identifier-vocabulary.test.d.ts +2 -0
  11. package/dist/__tests__/9dee06c4-patch-identifier-vocabulary.test.d.ts.map +1 -0
  12. package/dist/__tests__/9dee06c4-patch-identifier-vocabulary.test.js +130 -0
  13. package/dist/__tests__/9dee06c4-patch-identifier-vocabulary.test.js.map +1 -0
  14. package/dist/__tests__/dfe2d236-site-url-redirects-on-write.test.d.ts +31 -0
  15. package/dist/__tests__/dfe2d236-site-url-redirects-on-write.test.d.ts.map +1 -0
  16. package/dist/__tests__/dfe2d236-site-url-redirects-on-write.test.js +191 -0
  17. package/dist/__tests__/dfe2d236-site-url-redirects-on-write.test.js.map +1 -0
  18. package/dist/fse-operation-schema.d.ts +20 -0
  19. package/dist/fse-operation-schema.d.ts.map +1 -0
  20. package/dist/fse-operation-schema.js +75 -0
  21. package/dist/fse-operation-schema.js.map +1 -0
  22. package/dist/patch-identifier.d.ts +5 -0
  23. package/dist/patch-identifier.d.ts.map +1 -0
  24. package/dist/patch-identifier.js +87 -0
  25. package/dist/patch-identifier.js.map +1 -0
  26. package/dist/server.d.ts.map +1 -1
  27. package/dist/server.js +12 -8
  28. package/dist/server.js.map +1 -1
  29. package/dist/wordpress-client.d.ts +90 -9
  30. package/dist/wordpress-client.d.ts.map +1 -1
  31. package/dist/wordpress-client.js +447 -20
  32. package/dist/wordpress-client.js.map +1 -1
  33. package/package.json +1 -1
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Ticket 9dee06c4 (Nathan, n2workspaces.com, Divi 5.10.1, plugin 8.8.20):
3
+ * "apply-builder-patch cannot resolve that target by {"type":"id"} or
4
+ * {"type":"path"} (respira_patch_target_not_found) while find-element
5
+ * resolves both."
6
+ *
7
+ * respira_find_element takes identifier_type + identifier_value. A patch
8
+ * identifier is an object keyed by what you know ({ id }, { path }), where
9
+ * `type` means the MODULE type. The agent carried find_element's words over
10
+ * and sent { type: "id", value: "..." }, so the plugin searched for a module of
11
+ * type "id" and found none.
12
+ *
13
+ * Two things are checked here, both the way a client meets them:
14
+ * 1. The published tool schema states the accepted shapes: `type` is the
15
+ * module type, and find_element's vocabulary (`value`, `identifier_type`,
16
+ * `identifier_value`) is listed and described.
17
+ * 2. The real WordPressClient sends that vocabulary in the canonical shape,
18
+ * so it resolves even on a plugin that predates the plugin-side fix. What
19
+ * it must NOT touch is checked too: a real module type, a value-less
20
+ * identifier, and a conflict are forwarded exactly as sent.
21
+ */
22
+ import assert from 'node:assert/strict';
23
+ import { test, after } from 'node:test';
24
+ import { resolve } from 'node:path';
25
+ import http from 'node:http';
26
+ import { Client } from '@modelcontextprotocol/client';
27
+ import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
28
+ import { WordPressClient } from '../wordpress-client.js';
29
+ const servers = [];
30
+ after(() => {
31
+ for (const server of servers)
32
+ server.close();
33
+ });
34
+ async function listen(handler) {
35
+ const server = http.createServer(handler);
36
+ servers.push(server);
37
+ await new Promise((resolveListen) => server.listen(0, '127.0.0.1', resolveListen));
38
+ return server.address().port;
39
+ }
40
+ test('9dee06c4: the published apply_builder_patch schema states the accepted identifier shapes', { timeout: 20_000 }, async () => {
41
+ const port = await listen((_request, response) => {
42
+ response.writeHead(200, { 'content-type': 'application/json' });
43
+ response.end(JSON.stringify({ wordpress_version: '6.8', page_builder: { name: 'divi', version: '5.10.1' }, active_plugins: [] }));
44
+ });
45
+ const config = Buffer.from(JSON.stringify({
46
+ sites: [{ id: 'patch-ident', name: 'Patch identifier', url: `http://127.0.0.1:${port}`, apiKey: 'respira_patch_ident', default: true }],
47
+ preferences: { autoDuplicate: true, securityChecks: true },
48
+ })).toString('base64');
49
+ const client = new Client({ name: 'respira-9dee06c4', version: '1.0.0' });
50
+ try {
51
+ await client.connect(new StdioClientTransport({
52
+ command: process.execPath,
53
+ args: [resolve(process.cwd(), 'dist/index.js')],
54
+ cwd: process.cwd(),
55
+ stderr: 'pipe',
56
+ env: { ...process.env, RESPIRA_CONFIG_B64: config, RESPIRA_USAGE_OPT_OUT: '1', NODE_ENV: 'test' },
57
+ }));
58
+ const { tools } = await client.listTools();
59
+ const tool = tools.find((candidate) => /(^|_)apply_builder_patch$/.test(candidate.name));
60
+ assert.ok(tool, 'apply_builder_patch must be published');
61
+ const identifier = tool.inputSchema.properties?.operations?.items?.properties?.identifier;
62
+ assert.equal(identifier?.type, 'object', 'operations[].identifier must be an object');
63
+ const properties = identifier.properties ?? {};
64
+ for (const canonical of ['id', 'path', 'admin_label', 'type', 'match_content']) {
65
+ assert.ok(properties[canonical], `identifier.${canonical} must still be published`);
66
+ }
67
+ for (const alias of ['value', 'identifier_type', 'identifier_value']) {
68
+ assert.equal(properties[alias]?.type, 'string', `identifier.${alias} (find_element vocabulary) must be published as a string`);
69
+ assert.ok(String(properties[alias]?.description ?? '').length > 0, `identifier.${alias} must say what it is for`);
70
+ }
71
+ assert.match(String(identifier.description), /MODULE type/, 'the identifier description must say `type` is the module type');
72
+ assert.match(String(identifier.description), /identifier_type/, 'the identifier description must name find_element\'s vocabulary');
73
+ assert.match(String(tool.description), /\{ type: "id", value: "\.\.\." \}/, 'the tool description must show the find_element shape it accepts');
74
+ assert.match(String(tool.description), /respira_patch_ambiguous_target/, 'the tool description must say a shared id is refused');
75
+ }
76
+ finally {
77
+ await client.close();
78
+ }
79
+ });
80
+ test('9dee06c4: the client sends find_element vocabulary in the canonical identifier shape', async () => {
81
+ const bodies = [];
82
+ const port = await listen((request, response) => {
83
+ const url = request.url ?? '';
84
+ if (request.method === 'GET' && url.startsWith('/wp-json/respira/v2/status')) {
85
+ response.writeHead(200, { 'content-type': 'application/json' });
86
+ response.end(JSON.stringify({ ok: true }));
87
+ return;
88
+ }
89
+ if (request.method === 'POST' && url.startsWith('/wp-json/respira/v2/builder/divi/patch/42')) {
90
+ let raw = '';
91
+ request.on('data', (chunk) => { raw += chunk; });
92
+ request.on('end', () => {
93
+ bodies.push(JSON.parse(raw));
94
+ response.writeHead(200, { 'content-type': 'application/json' });
95
+ response.end(JSON.stringify({ success: true }));
96
+ });
97
+ return;
98
+ }
99
+ response.writeHead(404, { 'content-type': 'application/json' });
100
+ response.end(JSON.stringify({ code: 'not_found' }));
101
+ });
102
+ const wp = new WordPressClient({
103
+ id: 'patch-ident',
104
+ name: 'patch-ident',
105
+ url: `http://127.0.0.1:${port}`,
106
+ apiKey: 'respira_test_key',
107
+ });
108
+ const update = { content: '<p>New</p>' };
109
+ await wp.applyBuilderPatch('divi', 42, [
110
+ { identifier: { type: 'id', value: '2c3a794b-0000-4000-8000-000000000001' }, updates: update },
111
+ { identifier: { type: 'path', value: '0.2.1.0.1.3' }, updates: update },
112
+ { identifier: { identifier_type: 'admin_label', identifier_value: 'Hero' }, updates: update },
113
+ { identifier: { identifier_type: 'type', identifier_value: 'divi/text', match_content: 'Why us' }, updates: update },
114
+ // Must reach the plugin exactly as sent.
115
+ { identifier: { type: 'divi/text', match_content: 'Why us' }, updates: update },
116
+ { identifier: { type: 'id' }, updates: update },
117
+ { identifier: { type: 'id', value: 'aaa', id: 'bbb' }, updates: update },
118
+ ]);
119
+ assert.equal(bodies.length, 1, 'exactly one patch request is sent');
120
+ const identifiers = bodies[0].operations.map((operation) => operation.identifier);
121
+ assert.deepEqual(identifiers[0], { id: '2c3a794b-0000-4000-8000-000000000001' }, '{ type: "id", value } is sent as { id }');
122
+ assert.deepEqual(identifiers[1], { path: '0.2.1.0.1.3' }, '{ type: "path", value } is sent as { path }');
123
+ assert.deepEqual(identifiers[2], { admin_label: 'Hero' }, '{ identifier_type, identifier_value } is sent as the canonical key');
124
+ assert.deepEqual(identifiers[3], { type: 'divi/text', match_content: 'Why us' }, 'identifier_type "type" becomes the module type and keeps match_content');
125
+ assert.deepEqual(identifiers[4], { type: 'divi/text', match_content: 'Why us' }, 'a real module type is untouched');
126
+ assert.deepEqual(identifiers[5], { type: 'id' }, 'an identifier with no value is untouched (the plugin explains the shape)');
127
+ assert.deepEqual(identifiers[6], { type: 'id', value: 'aaa', id: 'bbb' }, 'a conflicting identifier is untouched (the plugin refuses it)');
128
+ assert.deepEqual(bodies[0].operations[0].updates, update, 'updates are forwarded unchanged');
129
+ });
130
+ //# sourceMappingURL=9dee06c4-patch-identifier-vocabulary.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"9dee06c4-patch-identifier-vocabulary.test.js","sourceRoot":"","sources":["../../src/__tests__/9dee06c4-patch-identifier-vocabulary.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Tickets dfe2d236, 6e94282d, addc289e, 77aae005, c2dd9d87 (staging3.naa.edu,
3
+ * mcp 8.3.20, plugin 8.8.13).
4
+ *
5
+ * The configured URL was the bare host. The web server 301s every request on
6
+ * it to the www host. Every read worked and every write failed, in 190 to
7
+ * 600ms, with "Could not connect ... no response received" and
8
+ * `respira_write_outcome_unknown`. diagnose_connection called the site fine.
9
+ *
10
+ * Mechanism (reproduced here against the real client, no adapter mocking):
11
+ *
12
+ * 1. guardAgainstMethodDowngrade() (ticket 1b0eda46, mcp 8.3.18) runs as the
13
+ * `beforeRedirect` hook and throws for a write that meets a 301/302/303.
14
+ * 2. follow-redirects catches that throw in `_processResponse`, wraps it as a
15
+ * RedirectionError (code ERR_FR_REDIRECTION_FAILURE, the guard's error as
16
+ * `.cause`) and emits it as a request 'error'.
17
+ * 3. axios turns a request 'error' into an AxiosError with `.request` and NO
18
+ * `.response`.
19
+ * 4. handleError's no-response branch therefore described a clean, immediate
20
+ * 301 answer as a dropped connection and a write of unknown outcome.
21
+ *
22
+ * Reads follow the redirect (the guard ignores them), which is why reads
23
+ * worked. And a 307/308 was followed for writes too, which re-sent the write,
24
+ * API key header included, to whatever host the Location named.
25
+ *
26
+ * The "www" host here is a second local server on a different port. Host and
27
+ * port together are what a URL's `host` compares, so this exercises the same
28
+ * code path as staging3.naa.edu -> www.staging3.naa.edu.
29
+ */
30
+ export {};
31
+ //# sourceMappingURL=dfe2d236-site-url-redirects-on-write.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dfe2d236-site-url-redirects-on-write.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/dfe2d236-site-url-redirects-on-write.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG"}
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Tickets dfe2d236, 6e94282d, addc289e, 77aae005, c2dd9d87 (staging3.naa.edu,
3
+ * mcp 8.3.20, plugin 8.8.13).
4
+ *
5
+ * The configured URL was the bare host. The web server 301s every request on
6
+ * it to the www host. Every read worked and every write failed, in 190 to
7
+ * 600ms, with "Could not connect ... no response received" and
8
+ * `respira_write_outcome_unknown`. diagnose_connection called the site fine.
9
+ *
10
+ * Mechanism (reproduced here against the real client, no adapter mocking):
11
+ *
12
+ * 1. guardAgainstMethodDowngrade() (ticket 1b0eda46, mcp 8.3.18) runs as the
13
+ * `beforeRedirect` hook and throws for a write that meets a 301/302/303.
14
+ * 2. follow-redirects catches that throw in `_processResponse`, wraps it as a
15
+ * RedirectionError (code ERR_FR_REDIRECTION_FAILURE, the guard's error as
16
+ * `.cause`) and emits it as a request 'error'.
17
+ * 3. axios turns a request 'error' into an AxiosError with `.request` and NO
18
+ * `.response`.
19
+ * 4. handleError's no-response branch therefore described a clean, immediate
20
+ * 301 answer as a dropped connection and a write of unknown outcome.
21
+ *
22
+ * Reads follow the redirect (the guard ignores them), which is why reads
23
+ * worked. And a 307/308 was followed for writes too, which re-sent the write,
24
+ * API key header included, to whatever host the Location named.
25
+ *
26
+ * The "www" host here is a second local server on a different port. Host and
27
+ * port together are what a URL's `host` compares, so this exercises the same
28
+ * code path as staging3.naa.edu -> www.staging3.naa.edu.
29
+ */
30
+ import { test, after } from 'node:test';
31
+ import assert from 'node:assert/strict';
32
+ import http from 'node:http';
33
+ import { WordPressClient } from '../wordpress-client.js';
34
+ const servers = [];
35
+ after(() => {
36
+ for (const s of servers)
37
+ s.close();
38
+ });
39
+ const WRITE_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE']);
40
+ function routeOf(rawUrl) {
41
+ const url = new URL(rawUrl, 'http://placeholder');
42
+ return url.searchParams.get('rest_route') || url.pathname.replace(/^\/wp-json/, '') || '/';
43
+ }
44
+ async function listen(server) {
45
+ servers.push(server);
46
+ await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
47
+ const { port } = server.address();
48
+ return `http://127.0.0.1:${port}`;
49
+ }
50
+ /** The canonical host: answers like WordPress with the Respira plugin. */
51
+ function wordpressLike(hits) {
52
+ return http.createServer((req, res) => {
53
+ req.resume();
54
+ req.on('end', () => {
55
+ hits.push({ method: req.method || '', url: req.url || '' });
56
+ const route = routeOf(req.url || '/');
57
+ const json = (status, body) => {
58
+ res.writeHead(status, { 'Content-Type': 'application/json' });
59
+ res.end(req.method === 'HEAD' ? undefined : JSON.stringify(body));
60
+ };
61
+ if (route === '/respira/v2/status')
62
+ return json(200, { status: 'ok' });
63
+ if (route === '/respira/v1/ping' && (req.method === 'GET' || req.method === 'HEAD')) {
64
+ return json(200, { ok: true, plugin: 'respira-for-wordpress' });
65
+ }
66
+ if (req.method === 'OPTIONS')
67
+ return json(200, {});
68
+ if (req.method === 'GET' || req.method === 'HEAD')
69
+ return json(200, { read: true, route });
70
+ // /ping registers no write handler, exactly like the plugin.
71
+ if (route === '/respira/v1/ping') {
72
+ return json(404, { code: 'rest_no_route', message: 'No route was found matching the URL and request method.', data: { status: 404 } });
73
+ }
74
+ return json(200, { written: true, method: req.method, route });
75
+ });
76
+ });
77
+ }
78
+ /**
79
+ * staging3.naa.edu: every request on the configured host is redirected to the
80
+ * www host, same path and query.
81
+ */
82
+ async function startCanonicalRedirectPair(status = 301) {
83
+ const wwwHits = [];
84
+ const bareHits = [];
85
+ const wwwOrigin = await listen(wordpressLike(wwwHits));
86
+ const bareOrigin = await listen(http.createServer((req, res) => {
87
+ req.resume();
88
+ req.on('end', () => {
89
+ bareHits.push({ method: req.method || '', url: req.url || '' });
90
+ res.writeHead(status, { Location: `${wwwOrigin}${req.url}`, 'Content-Type': 'text/html' });
91
+ res.end('<html><body>Moved</body></html>');
92
+ });
93
+ }));
94
+ const client = new WordPressClient({
95
+ id: 'dfe2d236',
96
+ name: 'dfe2d236',
97
+ url: bareOrigin,
98
+ apiKey: 'respira_test_key',
99
+ });
100
+ const wwwWrites = () => wwwHits.filter((h) => WRITE_METHODS.has(h.method) && !h.url.includes('/server/compatibility'));
101
+ return { client, wwwOrigin, bareOrigin, wwwHits, bareHits, wwwWrites };
102
+ }
103
+ const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
104
+ async function captureRejection(p) {
105
+ try {
106
+ const value = await p;
107
+ assert.fail(`expected a rejection, got a result: ${JSON.stringify(value)}`);
108
+ }
109
+ catch (err) {
110
+ if (err?.code === 'ERR_ASSERTION')
111
+ throw err;
112
+ return err;
113
+ }
114
+ }
115
+ function assertSiteUrlRedirect(err, wwwOrigin) {
116
+ assert.equal(err.name, 'respira_site_url_redirects', `unexpected error name ${err.name}: ${err.message}`);
117
+ assert.doesNotMatch(err.message, /no response received/i, 'a clean 301 answer is not a dropped connection');
118
+ assert.doesNotMatch(err.message, /may have completed/i, 'the outcome is known: nothing ran');
119
+ assert.match(err.message, new RegExp(`redirects to ${escapeRe(wwwOrigin)}`));
120
+ assert.match(err.message, /update the URL for this site in your Respira config/i);
121
+ const envelope = err.respiraErrorEnvelope;
122
+ assert.ok(envelope, 'structured fields reach the tool result');
123
+ assert.equal(envelope.suggested_url, wwwOrigin);
124
+ assert.ok(String(envelope.redirect_location).startsWith(wwwOrigin));
125
+ }
126
+ test('control: reads follow the redirect and keep working', async () => {
127
+ const site = await startCanonicalRedirectPair(301);
128
+ const result = await site.client.callRestV1('GET', '/ping');
129
+ assert.deepEqual(result, { ok: true, plugin: 'respira-for-wordpress' });
130
+ });
131
+ test('delete_page on a host that 301s to www names the redirect instead of "no response received"', async () => {
132
+ const site = await startCanonicalRedirectPair(301);
133
+ const err = await captureRejection(site.client.deletePage(5));
134
+ assertSiteUrlRedirect(err, site.wwwOrigin);
135
+ assert.equal(site.bareHits.filter((h) => h.method === 'DELETE').length, 1, 'one DELETE, answered by the redirect');
136
+ assert.equal(site.wwwWrites().length, 0, 'the write was never re-sent to the other host');
137
+ });
138
+ test('a PUT write (update_bricks_global_class) meets the same redirect: no POST fallback, no replay', async () => {
139
+ const site = await startCanonicalRedirectPair(301);
140
+ const err = await captureRejection(site.client.callRestV2('PUT', '/bricks/global-classes/zzprobe1', { name: 'zzprobe1' }));
141
+ assertSiteUrlRedirect(err, site.wwwOrigin);
142
+ const writesOnBare = site.bareHits.filter((h) => WRITE_METHODS.has(h.method) && h.url.includes('/bricks/global-classes/'));
143
+ assert.deepEqual(writesOnBare.map((h) => h.method), ['PUT'], 'no PUT->POST or method-override retry');
144
+ assert.equal(site.wwwWrites().length, 0);
145
+ });
146
+ test('a POST write meets the same redirect and says so', async () => {
147
+ const site = await startCanonicalRedirectPair(301);
148
+ const err = await captureRejection(site.client.callRestV1('POST', '/pages', { title: 'x' }));
149
+ assertSiteUrlRedirect(err, site.wwwOrigin);
150
+ assert.equal(site.wwwWrites().length, 0);
151
+ });
152
+ test('a 307 to another host is not followed for a write (it would replay the write there)', async () => {
153
+ const site = await startCanonicalRedirectPair(307);
154
+ const err = await captureRejection(site.client.deletePage(6));
155
+ assertSiteUrlRedirect(err, site.wwwOrigin);
156
+ assert.equal(site.wwwWrites().length, 0, 'the DELETE must not be replayed on the www host');
157
+ });
158
+ test('control: a same-host 307 on a write is still followed', async () => {
159
+ const hits = [];
160
+ const inner = wordpressLike(hits);
161
+ let origin = '';
162
+ const server = http.createServer((req, res) => {
163
+ if ((req.url || '').startsWith('/wp-json/respira/v1/old/')) {
164
+ req.resume();
165
+ req.on('end', () => {
166
+ res.writeHead(307, { Location: `${origin}${(req.url || '').replace('/old/', '/pages/')}` });
167
+ res.end();
168
+ });
169
+ return;
170
+ }
171
+ inner.emit('request', req, res);
172
+ });
173
+ origin = await listen(server);
174
+ const client = new WordPressClient({ id: 'same-host-307', name: 'same-host-307', url: origin, apiKey: 'respira_test_key' });
175
+ const result = await client.callRestV1('DELETE', '/old/9');
176
+ assert.equal(result.written, true);
177
+ assert.equal(result.route, '/respira/v1/pages/9');
178
+ });
179
+ test('diagnose_connection reports the canonical-host redirect instead of grading writes as fine', async () => {
180
+ const site = await startCanonicalRedirectPair(301);
181
+ const report = await site.client.diagnoseConnection({ probe_timeout_ms: 5000 });
182
+ assert.ok(report.site_url_redirect, 'site_url_redirect must be reported');
183
+ assert.equal(report.site_url_redirect.status, 301);
184
+ assert.ok(String(report.site_url_redirect.location).startsWith(site.wwwOrigin));
185
+ assert.equal(report.site_url_redirect.suggested_url, site.wwwOrigin);
186
+ assert.equal(report.write_method_verdict, 'site_url_redirects');
187
+ assert.equal(report.write_method_blocked, true, 'every write is refused, so writes are not fine');
188
+ assert.match(String(report.recommendations[0] || ''), new RegExp(escapeRe(site.wwwOrigin)));
189
+ assert.match(String(report.recommendations[0] || ''), /Respira config/i);
190
+ });
191
+ //# sourceMappingURL=dfe2d236-site-url-redirects-on-write.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dfe2d236-site-url-redirects-on-write.test.js","sourceRoot":"","sources":["../../src/__tests__/dfe2d236-site-url-redirects-on-write.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The shape of one entry in `operations` for update_site_template,
3
+ * update_site_pattern and update_site_navigation.
4
+ *
5
+ * All three route to Respira_FSE_Templates::apply_operations() in the plugin
6
+ * (class-respira-fse-templates.php), and every field below is a key that
7
+ * function reads. Nothing here is aspirational: if a field is added, the
8
+ * plugin has to read it first.
9
+ *
10
+ * Until ticket 303ad1de each operation was published as a bare
11
+ * `{ type: 'object' }`. The description listed the operation names but never
12
+ * said which key carries them, so agents guessed. The first guess is `type`,
13
+ * which the plugin refused with 422 "Operation 1: unsupported operation type".
14
+ * The plugin now also accepts `op` and `type`, but `operation` is the key this
15
+ * schema names, and it wins when more than one is sent.
16
+ */
17
+ /** The names apply_operations() dispatches on, in the plugin's order. */
18
+ export declare const FSE_OPERATION_TYPES: readonly ["set_attributes", "replace_block", "insert_before", "insert_after", "insert_inside", "remove_block", "move_block"];
19
+ export declare const FSE_OPERATION_ITEM_SCHEMA: Record<string, unknown>;
20
+ //# sourceMappingURL=fse-operation-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fse-operation-schema.d.ts","sourceRoot":"","sources":["../src/fse-operation-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,yEAAyE;AACzE,eAAO,MAAM,mBAAmB,8HAQtB,CAAC;AA4BX,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CA6B7D,CAAC"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * The shape of one entry in `operations` for update_site_template,
3
+ * update_site_pattern and update_site_navigation.
4
+ *
5
+ * All three route to Respira_FSE_Templates::apply_operations() in the plugin
6
+ * (class-respira-fse-templates.php), and every field below is a key that
7
+ * function reads. Nothing here is aspirational: if a field is added, the
8
+ * plugin has to read it first.
9
+ *
10
+ * Until ticket 303ad1de each operation was published as a bare
11
+ * `{ type: 'object' }`. The description listed the operation names but never
12
+ * said which key carries them, so agents guessed. The first guess is `type`,
13
+ * which the plugin refused with 422 "Operation 1: unsupported operation type".
14
+ * The plugin now also accepts `op` and `type`, but `operation` is the key this
15
+ * schema names, and it wins when more than one is sent.
16
+ */
17
+ /** The names apply_operations() dispatches on, in the plugin's order. */
18
+ export const FSE_OPERATION_TYPES = [
19
+ 'set_attributes',
20
+ 'replace_block',
21
+ 'insert_before',
22
+ 'insert_after',
23
+ 'insert_inside',
24
+ 'remove_block',
25
+ 'move_block',
26
+ ];
27
+ const blockPath = (description) => ({
28
+ type: 'array',
29
+ items: { type: 'integer', minimum: 0 },
30
+ minItems: 1,
31
+ description,
32
+ });
33
+ const FSE_BLOCK_SCHEMA = {
34
+ type: 'object',
35
+ description: 'A block in the shape get_site_template returns in `blocks`. WordPress saves a block from innerContent, not innerHTML.',
36
+ properties: {
37
+ blockName: { type: 'string', description: 'Block name, for example core/html or core/paragraph.' },
38
+ attrs: { type: 'object', description: 'Block attributes stored in the block comment.' },
39
+ innerContent: {
40
+ type: 'array',
41
+ items: { type: ['string', 'null'] },
42
+ description: 'What WordPress saves: markup strings, plus one null per inner block, in order. For core/html send the markup as ["<div>...</div>"]. When omitted it is taken from innerHTML (a block with no inner blocks) or set to one null per inner block.',
43
+ },
44
+ innerHTML: { type: 'string', description: 'Markup of a block with no inner blocks. Used only when innerContent is omitted.' },
45
+ innerBlocks: { type: 'array', items: { type: 'object' }, description: 'Child blocks, each in this same shape.' },
46
+ },
47
+ required: ['blockName'],
48
+ };
49
+ export const FSE_OPERATION_ITEM_SCHEMA = {
50
+ type: 'object',
51
+ description: 'One path-addressed block operation. Name it with `operation`. set_attributes needs path and attributes; replace_block, insert_before, insert_after and insert_inside need path and block; remove_block needs path; move_block needs from_path and to_path.',
52
+ properties: {
53
+ operation: {
54
+ type: 'string',
55
+ enum: [...FSE_OPERATION_TYPES],
56
+ description: 'Which operation to apply. `op` and `type` are accepted as the same key, but `operation` wins when several are sent.',
57
+ },
58
+ path: blockPath('Indexes into `blocks` from the get_site_template read, top level first, then innerBlocks. Top-level indexes count the whitespace entries (blockName null) that WordPress keeps between blocks.'),
59
+ expected_block_name: {
60
+ type: 'string',
61
+ description: 'Optional guard. The operation is refused with respira_fse_wrong_target if the block at path has a different blockName.',
62
+ },
63
+ attributes: { type: 'object', description: 'set_attributes: merged into the block attrs. Attributes not listed are kept.' },
64
+ block: {
65
+ ...FSE_BLOCK_SCHEMA,
66
+ description: 'replace_block: the block that replaces the one at path. insert_before and insert_after: the block to add next to it. insert_inside: the block to add as a child. ' +
67
+ FSE_BLOCK_SCHEMA.description,
68
+ },
69
+ position: { type: 'integer', minimum: 0, description: 'insert_inside: index among the children. Omit to append.' },
70
+ from_path: blockPath('move_block: path of the block to move. Defaults to path.'),
71
+ to_path: blockPath('move_block: the moved block lands before the block at this path, indexed after the block is removed from from_path.'),
72
+ },
73
+ required: ['operation'],
74
+ };
75
+ //# sourceMappingURL=fse-operation-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fse-operation-schema.js","sourceRoot":"","sources":["../src/fse-operation-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,yEAAyE;AACzE,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,gBAAgB;IAChB,eAAe;IACf,eAAe;IACf,cAAc;IACd,eAAe;IACf,cAAc;IACd,YAAY;CACJ,CAAC;AAEX,MAAM,SAAS,GAAG,CAAC,WAAmB,EAAE,EAAE,CAAC,CAAC;IAC1C,IAAI,EAAE,OAAO;IACb,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;IACtC,QAAQ,EAAE,CAAC;IACX,WAAW;CACZ,CAAC,CAAC;AAEH,MAAM,gBAAgB,GAAG;IACvB,IAAI,EAAE,QAAQ;IACd,WAAW,EACT,uHAAuH;IACzH,UAAU,EAAE;QACV,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,sDAAsD,EAAE;QAClG,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,+CAA+C,EAAE;QACvF,YAAY,EAAE;YACZ,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE;YACnC,WAAW,EACT,gPAAgP;SACnP;QACD,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,iFAAiF,EAAE;QAC7H,WAAW,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,wCAAwC,EAAE;KACjH;IACD,QAAQ,EAAE,CAAC,WAAW,CAAC;CACxB,CAAC;AAEF,MAAM,CAAC,MAAM,yBAAyB,GAA4B;IAChE,IAAI,EAAE,QAAQ;IACd,WAAW,EACT,4PAA4P;IAC9P,UAAU,EAAE;QACV,SAAS,EAAE;YACT,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,GAAG,mBAAmB,CAAC;YAC9B,WAAW,EAAE,qHAAqH;SACnI;QACD,IAAI,EAAE,SAAS,CACb,gMAAgM,CACjM;QACD,mBAAmB,EAAE;YACnB,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,wHAAwH;SACtI;QACD,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,8EAA8E,EAAE;QAC3H,KAAK,EAAE;YACL,GAAG,gBAAgB;YACnB,WAAW,EACT,mKAAmK;gBACnK,gBAAgB,CAAC,WAAW;SAC/B;QACD,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,0DAA0D,EAAE;QAClH,SAAS,EAAE,SAAS,CAAC,0DAA0D,CAAC;QAChF,OAAO,EAAE,SAAS,CAAC,qHAAqH,CAAC;KAC1I;IACD,QAAQ,EAAE,CAAC,WAAW,CAAC;CACxB,CAAC"}
@@ -0,0 +1,5 @@
1
+ /** Rewrite one patch identifier from find_element's vocabulary to the canonical key. */
2
+ export declare function normalizePatchIdentifier(identifier: unknown): unknown;
3
+ /** Apply normalizePatchIdentifier to every operation's identifier. */
4
+ export declare function normalizePatchOperations<T>(operations: T): T;
5
+ //# sourceMappingURL=patch-identifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch-identifier.d.ts","sourceRoot":"","sources":["../src/patch-identifier.ts"],"names":[],"mappings":"AAoCA,wFAAwF;AACxF,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,OAAO,GAAG,OAAO,CAyCrE;AAED,sEAAsE;AACtE,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC,CAU5D"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Ticket 9dee06c4 (Divi 5.10.1): an agent that had just resolved an element
3
+ * with respira_find_element (`identifier_type` + `identifier_value`) carried
4
+ * those words into respira_apply_builder_patch and sent
5
+ * `{ type: "id", value: "2c3a794b-..." }` and `{ type: "path", value: "0.2.1.0.1.3" }`.
6
+ * In a patch identifier `type` is the MODULE type, so the plugin searched for a
7
+ * module of type "id" and answered respira_patch_target_not_found.
8
+ *
9
+ * The plugin now reads that vocabulary itself (Respira_Builder_Patcher::
10
+ * normalize_identifier). This does the same rewrite before the request leaves,
11
+ * so the shapes the tool schema advertises also work against plugin versions
12
+ * that predate the fix. Keep the word list in step with
13
+ * Respira_Builder_Patcher::FIND_ELEMENT_IDENTIFIER_TYPES.
14
+ *
15
+ * Deliberately conservative: an identifier is only rewritten when the value is
16
+ * present and the canonical key is not already set. Anything else is sent as
17
+ * is, and the plugin decides (including refusing a conflict).
18
+ */
19
+ const FIND_ELEMENT_IDENTIFIER_TYPES = {
20
+ id: 'id',
21
+ path: 'path',
22
+ admin_label: 'admin_label',
23
+ type: 'type',
24
+ widget_type: 'type',
25
+ class: 'class',
26
+ content: 'content',
27
+ text: 'text',
28
+ uncode_shortcode_id: 'uncode_shortcode_id',
29
+ };
30
+ function isPlainObject(value) {
31
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
32
+ }
33
+ /** Rewrite one patch identifier from find_element's vocabulary to the canonical key. */
34
+ export function normalizePatchIdentifier(identifier) {
35
+ if (!isPlainObject(identifier)) {
36
+ return identifier;
37
+ }
38
+ let word;
39
+ let value;
40
+ let drop;
41
+ if (typeof identifier.identifier_type === 'string' && 'identifier_value' in identifier) {
42
+ word = identifier.identifier_type;
43
+ value = identifier.identifier_value;
44
+ drop = ['identifier_type', 'identifier_value'];
45
+ }
46
+ else if (typeof identifier.type === 'string' && 'value' in identifier) {
47
+ word = identifier.type;
48
+ value = identifier.value;
49
+ drop = ['type', 'value'];
50
+ }
51
+ else {
52
+ return identifier;
53
+ }
54
+ const key = FIND_ELEMENT_IDENTIFIER_TYPES[String(word).trim().toLowerCase()];
55
+ if (!key) {
56
+ return identifier;
57
+ }
58
+ if (typeof value !== 'string' && typeof value !== 'number') {
59
+ return identifier;
60
+ }
61
+ const text = String(value).trim();
62
+ if (text === '') {
63
+ return identifier;
64
+ }
65
+ const out = { ...identifier };
66
+ for (const name of drop) {
67
+ delete out[name];
68
+ }
69
+ if (key in out) {
70
+ return identifier;
71
+ }
72
+ out[key] = text;
73
+ return out;
74
+ }
75
+ /** Apply normalizePatchIdentifier to every operation's identifier. */
76
+ export function normalizePatchOperations(operations) {
77
+ if (!Array.isArray(operations)) {
78
+ return operations;
79
+ }
80
+ return operations.map((operation) => {
81
+ if (!isPlainObject(operation) || !('identifier' in operation)) {
82
+ return operation;
83
+ }
84
+ return { ...operation, identifier: normalizePatchIdentifier(operation.identifier) };
85
+ });
86
+ }
87
+ //# sourceMappingURL=patch-identifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch-identifier.js","sourceRoot":"","sources":["../src/patch-identifier.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAkBH,OAAO,KAAK,EAAE,mBAAmB,EAAe,MAAM,kBAAkB,CAAC;AAwCzE,MAAM,MAAM,eAAe,GAAG;IAC5B,0EAA0E;IAC1E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,0EAA0E;IAC1E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,4EAA4E;IAC5E,qBAAqB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,eAAe,EACtB,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAkBf;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,eAAe,EACtB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,eAAe,CAUjB;AAiFD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAQ5D;AAMD;;;;;;;;;;;;;;;;GAgBG;AACH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,GAAG,MAAM,CAoBhF;AA0LD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,aAkB3B,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,aAW3B,CAAC;AAEH,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,CAIhD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAIvE;AAED,qBAAa,sBAAsB;IACjC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAAgC;IACnD,OAAO,CAAC,KAAK,CAA2C;IACxD,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,YAAY,CAA4B;IAChD,8EAA8E;IAC9E,OAAO,CAAC,YAAY,CAA4B;IAChD,8EAA8E;IAC9E,OAAO,CAAC,mBAAmB,CAAS;IACpC,iFAAiF;IACjF,OAAO,CAAC,iBAAiB,CAAK;IAC9B;;;OAGG;IACH,OAAO,CAAC,qBAAqB,CAMK;IAClC,iFAAiF;IACjF,OAAO,CAAC,mBAAmB,CAAgC;IAC3D;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA6B;IAE5D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAsB;IAEhE;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAWzB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;gBA4Bb,WAAW,EAAE,mBAAmB,EAAE,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE;IAgWvE,OAAO,CAAC,cAAc;IAItB;;;;;;;;;OASG;IACH,OAAO,CAAC,oBAAoB;IAkB5B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,aAAa;IAiCrB,gEAAgE;IAChE,OAAO,CAAC,aAAa;IAUrB;;;;;;OAMG;YACW,UAAU;IA2CxB;;;;;;;;;;;;;;OAcG;YACW,WAAW;IAyJzB;;;;;;;;;OASG;YACW,kBAAkB;IA4PhC;;;;;;;;OAQG;IACH,OAAO,CAAC,qBAAqB;IAgE7B;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;YACW,qBAAqB;IAkBnC;;;;;;;OAOG;IACH,OAAO,CAAC,yBAAyB;YAiCnB,kBAAkB;IAwGhC,yFAAyF;IACzF,OAAO,CAAC,gBAAgB;IASxB;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,6BAA6B;IA6CrC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,yBAAyB;IA+BjC,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,aAAa;YAgUP,kBAAkB;YAsClB,yBAAyB;IASvC;;;OAGG;IACH,OAAO,CAAC,oBAAoB;YAyBd,QAAQ;IA22JtB;;;;;;OAMG;IACH;;;;;;;;;;;;;oDAagD;IAChD,OAAO,CAAC,mBAAmB,CAAoD;IAC/E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAU;IAClD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,wBAAwB,CAAU;IAC1D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,yBAAyB,CAAS;IAC1D;;mDAE+C;IAC/C,OAAO,CAAC,0BAA0B,CAAoC;IAEtE;;;;;;;;OAQG;YACW,0BAA0B;YAyC1B,oBAAoB;YA6CpB,2BAA2B;IAQzC;;;;OAIG;YACW,cAAc;IAQ5B,OAAO,CAAC,mBAAmB;IA4mE3B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAe3C;IAEF;;;;OAIG;IACH,OAAO,CAAC,0BAA0B;YAmCpB,cAAc;IA8H5B,oEAAoE;IACpE,OAAO,CAAC,iBAAiB;IAoBzB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,eAAe;YAuCT,gBAAgB;IAmgD9B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAsgBzB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAkWxB,GAAG;CA+CV"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}