drupal-mcp-connector 2.17.0 → 2.18.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/CHANGELOG.md +33 -0
- package/README.md +5 -0
- package/package.json +3 -1
- package/src/index.js +16 -8
- package/src/lib/backends/backend-interface.js +3 -0
- package/src/lib/backends/graphql.js +1 -0
- package/src/lib/backends/jsonapi.js +1 -0
- package/src/lib/dispatch.js +10 -0
- package/src/lib/draft-write.js +18 -429
- package/src/lib/module-tools.js +241 -0
- package/src/lib/node-draft-inventory.js +4 -47
- package/src/lib/operations.js +5 -0
- package/src/lib/patch-preflight.js +7 -15
- package/src/lib/sentinel-draft.js +530 -0
- package/src/lib/server-tools.js +50 -16
- package/src/lib/verify.js +7 -1
- package/src/tools/config.js +14 -4
- package/src/tools/media.js +14 -18
- package/src/tools/moderation.js +21 -21
- package/src/tools/nodes.js +1 -1
- package/src/tools/paragraphs.js +1 -1
- package/src/tools/reports-config.js +11 -1
- package/src/tools/reports-content.js +7 -6
- package/src/tools/revisions.js +24 -23
- package/src/tools/structure.js +1 -1
- package/src/tools/taxonomy.js +1 -1
- package/src/tools/translations.js +5 -3
package/src/lib/verify.js
CHANGED
|
@@ -738,7 +738,13 @@ export async function verifyLive(site, { transport, callTool = null, contentTarg
|
|
|
738
738
|
*/
|
|
739
739
|
const attemptConfigWrite = async () => {
|
|
740
740
|
try {
|
|
741
|
-
|
|
741
|
+
// The negative probe must reach source authorization, not merely prove
|
|
742
|
+
// that the connector hides out-of-tier tools from discovery.
|
|
743
|
+
const tool = site.serverTools?.bindings === undefined ? CONFIG_SET_TOOL
|
|
744
|
+
: (await import("./module-tools.js")).resolveModuleBinding(site, "configSet", {
|
|
745
|
+
operation: "write", scope: "mcp_config", capabilities: ["configWrite"],
|
|
746
|
+
}).policy.name;
|
|
747
|
+
await callTool(site, tool, { name: "system.site", data: { name: "verification probe" } });
|
|
742
748
|
return { served: true, outcome: "served", detail: "accepted" };
|
|
743
749
|
} catch (err) {
|
|
744
750
|
// Not every throw is a refusal — see classifyBridgeError.
|
package/src/tools/config.js
CHANGED
|
@@ -22,7 +22,17 @@ import {
|
|
|
22
22
|
assertConfigScope,
|
|
23
23
|
hasScope,
|
|
24
24
|
} from "../lib/security.js";
|
|
25
|
-
import { callServerTool, SERVER_TOOLS } from "../lib/server-tools.js";
|
|
25
|
+
import { callServerTool, callBoundModuleTool, SERVER_TOOLS } from "../lib/server-tools.js";
|
|
26
|
+
|
|
27
|
+
/** Compatibility names use an approved module binding when configured. */
|
|
28
|
+
async function configTool(site, binding, args, operation, capability) {
|
|
29
|
+
if (site.serverTools?.bindings !== undefined) {
|
|
30
|
+
return callBoundModuleTool(site, binding, args, {
|
|
31
|
+
operation, scope: "mcp_config", capabilities: [capability],
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return callServerTool(site, new Map(Object.entries(SERVER_TOOLS)).get(binding), args);
|
|
35
|
+
}
|
|
26
36
|
|
|
27
37
|
// ---------------------------------------------------------------------------
|
|
28
38
|
// Config tools (governed via the server-tool bridge)
|
|
@@ -38,7 +48,7 @@ async function configGet({ site: siteName, name }) {
|
|
|
38
48
|
const site = getSiteConfig(siteName);
|
|
39
49
|
assertConfigScope(site, `config:get ${name}`);
|
|
40
50
|
assertConfigReadAllowed(resolveSecurityConfig(site));
|
|
41
|
-
return
|
|
51
|
+
return configTool(site, "configGet", { name }, "read", "configRead");
|
|
42
52
|
}
|
|
43
53
|
|
|
44
54
|
/**
|
|
@@ -52,7 +62,7 @@ async function configList({ site: siteName, prefix }) {
|
|
|
52
62
|
assertConfigScope(site, "config:list");
|
|
53
63
|
assertConfigReadAllowed(resolveSecurityConfig(site));
|
|
54
64
|
const args = prefix ? { prefix } : {};
|
|
55
|
-
return
|
|
65
|
+
return configTool(site, "configList", args, "read", "configRead");
|
|
56
66
|
}
|
|
57
67
|
|
|
58
68
|
/**
|
|
@@ -73,7 +83,7 @@ async function configSet({ site: siteName, name, value }) {
|
|
|
73
83
|
assertConfigScope(site, `config:set ${name}`);
|
|
74
84
|
assertNotReadOnly(sec, `config:set ${name}`);
|
|
75
85
|
assertConfigWriteAllowed(sec);
|
|
76
|
-
return
|
|
86
|
+
return configTool(site, "configSet", { name, data: value }, "write", "configWrite");
|
|
77
87
|
}
|
|
78
88
|
|
|
79
89
|
// ---------------------------------------------------------------------------
|
package/src/tools/media.js
CHANGED
|
@@ -15,9 +15,9 @@ import {
|
|
|
15
15
|
assertReadAllowed, assertWriteAllowed, assertDeleteAllowed, assertPublishAllowed,
|
|
16
16
|
} from "../lib/security.js";
|
|
17
17
|
import {
|
|
18
|
-
assertDraftLangcode, readDraftTranslation, readTranslationInventory,
|
|
19
|
-
} from "../lib/draft
|
|
20
|
-
import {
|
|
18
|
+
assertDraftLangcode, readDraftTranslation, readTranslationInventory,
|
|
19
|
+
} from "../lib/sentinel-draft.js";
|
|
20
|
+
import { prepareGuardedPatch, updateEntityGuarded } from "../lib/patch-preflight.js";
|
|
21
21
|
import { entityRevisionId } from "../lib/write-revision.js";
|
|
22
22
|
|
|
23
23
|
/**
|
|
@@ -138,26 +138,22 @@ async function updateMedia({ site: siteName, type, id, name, status, fields = {}
|
|
|
138
138
|
const backend = await resolveBackend(site);
|
|
139
139
|
if (langcode) {
|
|
140
140
|
const targetLang = assertDraftLangcode(langcode);
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
"Create it with drupal_create_translation first; a canonical langcode PATCH is not attempted.",
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
const result = await writeDraft(backend, {
|
|
152
|
-
entityType: "media", bundle: type, id, attributes, langcode: targetLang,
|
|
141
|
+
const patchTarget = await prepareGuardedPatch(backend, {
|
|
142
|
+
entityType: "media", bundle: type, id, attributes,
|
|
143
|
+
...(relationships ? { relationships } : {}),
|
|
144
|
+
langcode: targetLang,
|
|
145
|
+
});
|
|
146
|
+
const result = await updateEntityGuarded(backend, {
|
|
147
|
+
entityType: "media", bundle: type, id, attributes,
|
|
153
148
|
...(relationships ? { relationships } : {}),
|
|
154
|
-
|
|
149
|
+
langcode: targetLang,
|
|
150
|
+
...(patchTarget.draftRevision ? { draftRevision: patchTarget.draftRevision } : {}),
|
|
155
151
|
});
|
|
156
152
|
const redacted = redactCanonicalEntity(result, sec, "media");
|
|
157
|
-
const working = entityRevisionId(result) ?? workingVid;
|
|
153
|
+
const working = entityRevisionId(result) ?? patchTarget.workingVid;
|
|
158
154
|
return {
|
|
159
155
|
...redacted,
|
|
160
|
-
_revisions: { live: liveVid, working },
|
|
156
|
+
_revisions: { live: patchTarget.liveVid, working },
|
|
161
157
|
};
|
|
162
158
|
}
|
|
163
159
|
// #171: pre-read so an unrequested published-state flip is reported, not silent.
|
package/src/tools/moderation.js
CHANGED
|
@@ -22,7 +22,9 @@ import {
|
|
|
22
22
|
} from "../lib/security.js";
|
|
23
23
|
import { collectEntities } from "../lib/reports-support.js";
|
|
24
24
|
import { prepareGuardedPatch, updateEntityGuarded } from "../lib/patch-preflight.js";
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
assertDraftLangcode, isMissingTranslationEndpoint, readTranslationInventory, supportsSentinelDraft,
|
|
27
|
+
} from "../lib/sentinel-draft.js";
|
|
26
28
|
import { inventoryRowMatching } from "../lib/translation-rows.js";
|
|
27
29
|
|
|
28
30
|
/** Cap for the client-side scan when JSON:API cannot filter the field. */
|
|
@@ -65,25 +67,23 @@ async function setModerationState({ site: siteName, type, id, state, langcode })
|
|
|
65
67
|
const attributes = { moderation_state: state };
|
|
66
68
|
assertPublishAllowed(sec, attributes);
|
|
67
69
|
const backend = await resolveBackend(site);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
existing = null;
|
|
75
|
-
}
|
|
76
|
-
const patchTarget = await prepareGuardedPatch(backend, {
|
|
77
|
-
entityType: "node", bundle: type, id, existing, attributes, langcode: targetLang,
|
|
78
|
-
});
|
|
79
|
-
const patched = await updateEntityGuarded(backend, {
|
|
80
|
-
entityType: "node", bundle: type, id, attributes, langcode: targetLang,
|
|
81
|
-
...(patchTarget.draftRevision ? { draftRevision: patchTarget.draftRevision } : {}),
|
|
82
|
-
});
|
|
83
|
-
return redactCanonicalEntity(patched, sec, "node");
|
|
70
|
+
const targetLang = langcode ? assertDraftLangcode(langcode) : undefined;
|
|
71
|
+
let existing = null;
|
|
72
|
+
try {
|
|
73
|
+
existing = (await backend.getEntity({ entityType: "node", bundle: type, id })) ?? null;
|
|
74
|
+
} catch {
|
|
75
|
+
existing = null;
|
|
84
76
|
}
|
|
85
|
-
const
|
|
86
|
-
|
|
77
|
+
const patchTarget = await prepareGuardedPatch(backend, {
|
|
78
|
+
entityType: "node", bundle: type, id, existing, attributes,
|
|
79
|
+
...(targetLang ? { langcode: targetLang } : {}),
|
|
80
|
+
});
|
|
81
|
+
const patched = await updateEntityGuarded(backend, {
|
|
82
|
+
entityType: "node", bundle: type, id, attributes,
|
|
83
|
+
...(targetLang ? { langcode: targetLang } : {}),
|
|
84
|
+
...(patchTarget.draftRevision ? { draftRevision: patchTarget.draftRevision } : {}),
|
|
85
|
+
});
|
|
86
|
+
return redactCanonicalEntity(patched, sec, "node");
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/**
|
|
@@ -104,7 +104,7 @@ async function contentByModerationState({ site: siteName, type, state, limit = 2
|
|
|
104
104
|
const sort = [{ field: "changed", dir: "desc" }];
|
|
105
105
|
const targetLang = langcode ? assertDraftLangcode(langcode) : null;
|
|
106
106
|
if (targetLang) {
|
|
107
|
-
if (
|
|
107
|
+
if (!supportsSentinelDraft(backend)) {
|
|
108
108
|
return {
|
|
109
109
|
type, state, langcode: targetLang, unavailable: true,
|
|
110
110
|
reason: "A langcode filter requires Sentinel's translation inventory.",
|
|
@@ -130,7 +130,7 @@ async function contentByModerationState({ site: siteName, type, state, limit = 2
|
|
|
130
130
|
});
|
|
131
131
|
}
|
|
132
132
|
} catch (error) {
|
|
133
|
-
if (
|
|
133
|
+
if (isMissingTranslationEndpoint(error)) {
|
|
134
134
|
return {
|
|
135
135
|
type, state, langcode: targetLang, unavailable: true,
|
|
136
136
|
reason: "A langcode filter requires Sentinel's translation inventory.",
|
package/src/tools/nodes.js
CHANGED
|
@@ -18,7 +18,7 @@ import { shapeWriteResponse, flagUnrequestedStatusChange, RETURNING_SCHEMA, omit
|
|
|
18
18
|
import { resolveErrRelationships, relationshipsWereSent, paragraphPinsFromEntity } from "../lib/err-relationships.js";
|
|
19
19
|
import { attachWrittenRevisionPair, readWrittenRevision } from "../lib/write-revision.js";
|
|
20
20
|
import { prepareGuardedPatch, updateEntityGuarded } from "../lib/patch-preflight.js";
|
|
21
|
-
import { assertDraftLangcode, readDraftTranslation, readTranslationInventory } from "../lib/draft
|
|
21
|
+
import { assertDraftLangcode, readDraftTranslation, readTranslationInventory } from "../lib/sentinel-draft.js";
|
|
22
22
|
import { paragraphResourceVersion } from "./paragraphs.js";
|
|
23
23
|
import { assertBodySummaryWritable, attachSummaryDeprecation } from "../lib/body-summary.js";
|
|
24
24
|
import { buildRedirectAttributes, REDIRECT_ENTITY_TYPE } from "./redirects.js";
|
package/src/tools/paragraphs.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
import { getSiteConfig } from "../lib/config.js";
|
|
21
21
|
import { resolveSecurityConfig, assertConfigReadAllowed } from "../lib/security.js";
|
|
22
22
|
import { gatedReport } from "../lib/reports-support.js";
|
|
23
|
-
import { callServerTool, SERVER_TOOLS, toolResultData } from "../lib/server-tools.js";
|
|
23
|
+
import { callServerTool, callBoundModuleTool, SERVER_TOOLS, toolResultData } from "../lib/server-tools.js";
|
|
24
24
|
import { runPrivileged, serverToolsConfigured, drushConfigured } from "../lib/audit-sources.js";
|
|
25
25
|
import { sshDrush, parseDrush } from "./drush.js";
|
|
26
26
|
|
|
@@ -65,6 +65,11 @@ function configSourceAvailable(site) {
|
|
|
65
65
|
* @returns {Promise<*>} Parsed config object (or null/text).
|
|
66
66
|
*/
|
|
67
67
|
async function readConfig(site, name) {
|
|
68
|
+
if (site.serverTools?.bindings !== undefined) {
|
|
69
|
+
return toolResultData(await callBoundModuleTool(site, "configGet", { name }, {
|
|
70
|
+
operation: "read", scope: "mcp_config", capabilities: ["configRead"],
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
68
73
|
if (serverToolsConfigured(site)) {
|
|
69
74
|
try { return toolResultData(await callServerTool(site, SERVER_TOOLS.configGet, { name })); }
|
|
70
75
|
catch (err) { if (!drushConfigured(site)) throw err; }
|
|
@@ -82,6 +87,11 @@ async function readConfig(site, name) {
|
|
|
82
87
|
* @returns {Promise<string[]>} Config object names.
|
|
83
88
|
*/
|
|
84
89
|
async function listConfigNames(site, prefix) {
|
|
90
|
+
if (site.serverTools?.bindings !== undefined) {
|
|
91
|
+
return pickConfigNames(toolResultData(await callBoundModuleTool(site, "configList", { prefix }, {
|
|
92
|
+
operation: "read", scope: "mcp_config", capabilities: ["configRead"],
|
|
93
|
+
})));
|
|
94
|
+
}
|
|
85
95
|
if (serverToolsConfigured(site)) {
|
|
86
96
|
try { return pickConfigNames(toolResultData(await callServerTool(site, SERVER_TOOLS.configList, { prefix }))); }
|
|
87
97
|
catch (err) { if (!drushConfigured(site)) throw err; }
|
|
@@ -17,7 +17,9 @@ import { resolveBackend } from "../lib/backends/index.js";
|
|
|
17
17
|
import { resolveSecurityConfig, assertReadAllowed } from "../lib/security.js";
|
|
18
18
|
import { collectEntities, fieldValue, daysSince } from "../lib/reports-support.js";
|
|
19
19
|
import { bodyHtml, extractAnchors, classifyLink, normalizePath } from "../lib/audit-support.js";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
assertDraftLangcode, isMissingTranslationEndpoint, readTranslationInventory, supportsSentinelDraft,
|
|
22
|
+
} from "../lib/sentinel-draft.js";
|
|
21
23
|
import { inventoryRowMatching, inventoryTranslationRows, mapTranslationRow } from "../lib/translation-rows.js";
|
|
22
24
|
|
|
23
25
|
// ---------------------------------------------------------------------------
|
|
@@ -116,7 +118,7 @@ async function workflowBottlenecks({ site: siteName, type, days = 30, states, sa
|
|
|
116
118
|
sampleSize
|
|
117
119
|
);
|
|
118
120
|
|
|
119
|
-
if (targetLang && (
|
|
121
|
+
if (targetLang && !supportsSentinelDraft(backend)) {
|
|
120
122
|
return {
|
|
121
123
|
contentType,
|
|
122
124
|
unavailable: true,
|
|
@@ -139,7 +141,7 @@ async function workflowBottlenecks({ site: siteName, type, days = 30, states, sa
|
|
|
139
141
|
if (!row) continue;
|
|
140
142
|
state = row.moderation_state;
|
|
141
143
|
} catch (error) {
|
|
142
|
-
if (
|
|
144
|
+
if (isMissingTranslationEndpoint(error)) {
|
|
143
145
|
return {
|
|
144
146
|
contentType,
|
|
145
147
|
unavailable: true,
|
|
@@ -207,8 +209,7 @@ async function translationCoverage({ site: siteName, type, sampleSize = 100 }) {
|
|
|
207
209
|
sampleSize
|
|
208
210
|
);
|
|
209
211
|
|
|
210
|
-
|
|
211
|
-
if (typeof backend.rawQuery !== "function" || typeof backend.resourcePath !== "function") {
|
|
212
|
+
if (!supportsSentinelDraft(backend)) {
|
|
212
213
|
return {
|
|
213
214
|
contentType,
|
|
214
215
|
scanned: nodes.length,
|
|
@@ -239,7 +240,7 @@ async function translationCoverage({ site: siteName, type, sampleSize = 100 }) {
|
|
|
239
240
|
outdated: rows.some((row) => row.outdated === true),
|
|
240
241
|
});
|
|
241
242
|
} catch (error) {
|
|
242
|
-
if (!
|
|
243
|
+
if (!isMissingTranslationEndpoint(error)) throw error;
|
|
243
244
|
}
|
|
244
245
|
}
|
|
245
246
|
|
package/src/tools/revisions.js
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
* full chronological history enumeration requires the Drush bridge.
|
|
12
12
|
* - drupal_get_revision fetches one specific version (by vid or alias).
|
|
13
13
|
* - drupal_revert_revision is a GOVERNED WRITE: it reads the target revision
|
|
14
|
-
* and replays its editable attributes through
|
|
15
|
-
*
|
|
14
|
+
* and replays its editable attributes through prepareGuardedPatch /
|
|
15
|
+
* updateEntityGuarded (assertWriteAllowed gates it; internal/immutable
|
|
16
16
|
* bookkeeping fields are never written back).
|
|
17
17
|
*
|
|
18
18
|
* Reads are redacted per the site's security policy.
|
|
@@ -23,9 +23,11 @@ import { resolveBackend } from "../lib/backends/index.js";
|
|
|
23
23
|
import {
|
|
24
24
|
resolveSecurityConfig, assertReadAllowed, assertWriteAllowed, redactCanonicalEntity,
|
|
25
25
|
} from "../lib/security.js";
|
|
26
|
-
import {
|
|
27
|
-
|
|
26
|
+
import {
|
|
27
|
+
assertDraftLangcode, readDraftTranslation, readNodeDraftInventory, readTranslationInventory,
|
|
28
|
+
} from "../lib/sentinel-draft.js";
|
|
28
29
|
import { omitLiveComputedMetatag } from "../lib/entity-response.js";
|
|
30
|
+
import { changedAheadOfRevision, prepareGuardedPatch, updateEntityGuarded } from "../lib/patch-preflight.js";
|
|
29
31
|
|
|
30
32
|
// Attributes that describe the entity's identity / revision bookkeeping / paths.
|
|
31
33
|
// These are read-only or server-managed and must NOT be replayed on a revert.
|
|
@@ -123,20 +125,6 @@ function summarizeRevision(resource) {
|
|
|
123
125
|
};
|
|
124
126
|
}
|
|
125
127
|
|
|
126
|
-
/**
|
|
127
|
-
* Whether the default revision's `changed` is later than its own
|
|
128
|
-
* `revision_timestamp` — the readable fingerprint of a #201 node.
|
|
129
|
-
* @param {?object} summary
|
|
130
|
-
* @returns {boolean}
|
|
131
|
-
*/
|
|
132
|
-
function changedAheadOfRevision(summary) {
|
|
133
|
-
if (!summary?.changed || !summary?.revisionTimestamp) return false;
|
|
134
|
-
const changed = Date.parse(summary.changed);
|
|
135
|
-
const rev = Date.parse(summary.revisionTimestamp);
|
|
136
|
-
if (!Number.isFinite(changed) || !Number.isFinite(rev)) return false;
|
|
137
|
-
return changed > rev;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
128
|
const LIST_REVISIONS_BASE_NOTE =
|
|
141
129
|
"JSON:API only addresses revisions by id (id:<vid>) or the rel:latest-version / " +
|
|
142
130
|
"rel:working-copy aliases; it cannot enumerate the full chronological history. " +
|
|
@@ -286,10 +274,11 @@ async function getRevision({ site: siteName, type, id, version, langcode }) {
|
|
|
286
274
|
|
|
287
275
|
/**
|
|
288
276
|
* Revert an entity to a prior revision (GOVERNED WRITE). Reads the target
|
|
289
|
-
* revision, then replays its editable attributes through
|
|
290
|
-
* become the new current revision. Internal/
|
|
291
|
-
* (drupal_internal__*, revision metadata,
|
|
292
|
-
* stripped before the write so only
|
|
277
|
+
* revision, then replays its editable attributes through prepareGuardedPatch
|
|
278
|
+
* / updateEntityGuarded so they become the new current revision. Internal /
|
|
279
|
+
* immutable bookkeeping fields (drupal_internal__*, revision metadata,
|
|
280
|
+
* computed path, timestamps) are stripped before the write so only
|
|
281
|
+
* restorable content is sent.
|
|
293
282
|
*
|
|
294
283
|
* This does not delete any history — the revert produces a NEW revision whose
|
|
295
284
|
* content matches the target, preserving the audit trail.
|
|
@@ -328,7 +317,19 @@ async function revertRevision({ site: siteName, type, id, version }) {
|
|
|
328
317
|
)
|
|
329
318
|
);
|
|
330
319
|
|
|
331
|
-
|
|
320
|
+
let existing = null;
|
|
321
|
+
try {
|
|
322
|
+
existing = (await backend.getEntity({ entityType: "node", bundle: type, id })) ?? null;
|
|
323
|
+
} catch {
|
|
324
|
+
existing = null;
|
|
325
|
+
}
|
|
326
|
+
const patchTarget = await prepareGuardedPatch(backend, {
|
|
327
|
+
entityType: "node", bundle: type, id, existing, attributes,
|
|
328
|
+
});
|
|
329
|
+
await updateEntityGuarded(backend, {
|
|
330
|
+
entityType: "node", bundle: type, id, attributes,
|
|
331
|
+
...(patchTarget.draftRevision ? { draftRevision: patchTarget.draftRevision } : {}),
|
|
332
|
+
});
|
|
332
333
|
|
|
333
334
|
return {
|
|
334
335
|
success: true,
|
package/src/tools/structure.js
CHANGED
|
@@ -19,7 +19,7 @@ import { resolveBackend } from "../lib/backends/index.js";
|
|
|
19
19
|
import {
|
|
20
20
|
resolveSecurityConfig, assertReadAllowed, assertWriteAllowed, redactCanonicalEntity,
|
|
21
21
|
} from "../lib/security.js";
|
|
22
|
-
import { assertDraftLangcode } from "../lib/draft
|
|
22
|
+
import { assertDraftLangcode } from "../lib/sentinel-draft.js";
|
|
23
23
|
|
|
24
24
|
const MENU_LINK_TYPE = "menu_link_content";
|
|
25
25
|
const BLOCK_TYPE = "block_content";
|
package/src/tools/taxonomy.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
resolveSecurityConfig, redactCanonicalEntity,
|
|
13
13
|
assertReadAllowed, assertWriteAllowed, assertDeleteAllowed,
|
|
14
14
|
} from "../lib/security.js";
|
|
15
|
-
import { assertDraftLangcode } from "../lib/draft
|
|
15
|
+
import { assertDraftLangcode } from "../lib/sentinel-draft.js";
|
|
16
16
|
|
|
17
17
|
// ---------------------------------------------------------------------------
|
|
18
18
|
// Implementations
|
|
@@ -34,9 +34,11 @@ import { paragraphRevisionId } from "../lib/err-relationships.js";
|
|
|
34
34
|
import {
|
|
35
35
|
assertDraftLangcode,
|
|
36
36
|
createTranslationDraft,
|
|
37
|
+
isMissingTranslationEndpoint,
|
|
37
38
|
readTranslationInventory,
|
|
38
39
|
resolveNodeTranslationPair,
|
|
39
|
-
|
|
40
|
+
supportsSentinelDraft,
|
|
41
|
+
} from "../lib/sentinel-draft.js";
|
|
40
42
|
import { mapTranslationRow } from "../lib/translation-rows.js";
|
|
41
43
|
|
|
42
44
|
const LIST_NOTE =
|
|
@@ -66,7 +68,7 @@ async function listTranslations({ site: siteName, entityType = "node", type, id
|
|
|
66
68
|
|
|
67
69
|
const backend = await resolveBackend(site);
|
|
68
70
|
if ((entityType === "node" || entityType === "paragraph" || entityType === "media")
|
|
69
|
-
&&
|
|
71
|
+
&& supportsSentinelDraft(backend)) {
|
|
70
72
|
try {
|
|
71
73
|
const meta = await readTranslationInventory(backend, { entityType, bundle: type, id });
|
|
72
74
|
const liveLangs = (meta.live?.translations ?? []).map((row) => row.langcode);
|
|
@@ -84,7 +86,7 @@ async function listTranslations({ site: siteName, entityType = "node", type, id
|
|
|
84
86
|
note: LIST_NOTE,
|
|
85
87
|
};
|
|
86
88
|
} catch (error) {
|
|
87
|
-
if (
|
|
89
|
+
if (!isMissingTranslationEndpoint(error)) {
|
|
88
90
|
throw error;
|
|
89
91
|
}
|
|
90
92
|
}
|