@abinnovision/payloadcms-mcpx 2.0.0-beta.15 → 2.0.0-beta.18
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 +14 -13
- package/dist/builtin-tool-names.mjs +1 -0
- package/dist/endpoint/server.mjs +5 -1
- package/dist/tools/builtin.mjs +2 -0
- package/dist/tools/create-document.mjs +37 -20
- package/dist/tools/duplicate-document.mjs +49 -0
- package/dist/tools/links.mjs +52 -0
- package/dist/tools/patch-document.mjs +7 -1
- package/dist/tools/publish-document.mjs +7 -1
- package/dist/types.d.mts +3 -2
- package/dist/write/draft-guard.mjs +6 -1
- package/dist/write/publish-intent.mjs +9 -1
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -116,19 +116,20 @@ every check.
|
|
|
116
116
|
|
|
117
117
|
## Tools
|
|
118
118
|
|
|
119
|
-
| Tool
|
|
120
|
-
|
|
|
121
|
-
| `listCapabilities`
|
|
122
|
-
| `describeSchema`
|
|
123
|
-
| `findDocuments`
|
|
124
|
-
| `getDocument`
|
|
125
|
-
| `findVersions`
|
|
126
|
-
| `patchDocument`
|
|
127
|
-
| `createDocument`
|
|
128
|
-
| `
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
131
|
-
| `
|
|
119
|
+
| Tool | What it does | Needs |
|
|
120
|
+
| ------------------- | ------------------------------------------------------------------ | --------- |
|
|
121
|
+
| `listCapabilities` | Lists what this key may do. Clients call it first. | any key |
|
|
122
|
+
| `describeSchema` | Describes the fields at one schema path, with the paths below it. | `read` |
|
|
123
|
+
| `findDocuments` | Queries a collection with a Payload `where`, `sort` and `select`. | `read` |
|
|
124
|
+
| `getDocument` | Reads a document, a subtree of it, an old version or a diff. | `read` |
|
|
125
|
+
| `findVersions` | Lists the version history of a document or global, without bodies. | `read` |
|
|
126
|
+
| `patchDocument` | Applies JSON Patch operations to the current draft, or a new file. | `write` |
|
|
127
|
+
| `createDocument` | Creates a draft from a seed, with a file in an upload collection. | `write` |
|
|
128
|
+
| `duplicateDocument` | Copies a document into a new draft, in every locale. | `write` |
|
|
129
|
+
| `validateDocument` | Lists what blocks publishing, without saving. | `write` |
|
|
130
|
+
| `publishDocument` | Publishes the current draft. | `publish` |
|
|
131
|
+
| `deleteDocument` | Asks to delete a document, which the key's user approves. | `delete` |
|
|
132
|
+
| `runConfirmed` | Runs the approved calls. | `delete` |
|
|
132
133
|
|
|
133
134
|
Collections and globals use the same tools. A tool that addresses one document takes either
|
|
134
135
|
`collection` and `id`, or `global` alone. [`docs/concepts.md`](./docs/concepts.md#tools) lists
|
package/dist/endpoint/server.mjs
CHANGED
package/dist/tools/builtin.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createDocument } from "./create-document.mjs";
|
|
2
2
|
import { deleteDocument } from "./delete-document.mjs";
|
|
3
3
|
import { describeSchema } from "./describe-schema.mjs";
|
|
4
|
+
import { duplicateDocument } from "./duplicate-document.mjs";
|
|
4
5
|
import { findDocuments } from "./find-documents.mjs";
|
|
5
6
|
import { findVersions } from "./find-versions.mjs";
|
|
6
7
|
import { getDocument } from "./get-document.mjs";
|
|
@@ -23,6 +24,7 @@ import { validateDocument } from "./validate-document.mjs";
|
|
|
23
24
|
findVersions,
|
|
24
25
|
patchDocument,
|
|
25
26
|
createDocument,
|
|
27
|
+
duplicateDocument,
|
|
26
28
|
validateDocument,
|
|
27
29
|
publishDocument,
|
|
28
30
|
deleteDocument,
|
|
@@ -6,6 +6,7 @@ import { collectPublishBlockers } from "../write/publish-blockers.mjs";
|
|
|
6
6
|
import { assertAcceptsFile, fileShape, fileWriteRefusal, uploadHandoff, uploadedFile } from "../upload/file.mjs";
|
|
7
7
|
import { liveWriteSentence, localeOf, localeShape, patchOnlySlugs, slugEnum, slugsFor } from "./shared.mjs";
|
|
8
8
|
import { readDraft, resolveEntity } from "./document.mjs";
|
|
9
|
+
import { documentLinks } from "./links.mjs";
|
|
9
10
|
import { stripRowIds } from "../write/row-ids.mjs";
|
|
10
11
|
import { z } from "zod";
|
|
11
12
|
import { APIError } from "payload";
|
|
@@ -14,10 +15,40 @@ const uploadSentence = (scope) => {
|
|
|
14
15
|
const slugs = patchOnlySlugs(scope);
|
|
15
16
|
return slugs.length === 0 ? "" : ` Documents in ${slugs.join(", ")} are files and cannot be created here. Upload the file in the admin panel, then edit its fields with patchDocument.`;
|
|
16
17
|
};
|
|
17
|
-
const DESCRIPTION = (scope) => `Creates a document in a collection. Use it only when the document does not exist yet. Returns "id", "status", "updatedAt" and, if any, "publishBlockers". "publishBlockersUnavailable" means that check failed. "data" may leave required fields empty for patchDocument to fill later, except in a collection whose listCapabilities entry has "draftValidation" true or "drafts" false. A field describeSchema does not list is refused, and "id" is always assigned.
|
|
18
|
+
const DESCRIPTION = (scope) => `Creates a document in a collection. Use it only when the document does not exist yet. Returns "id", "status", "updatedAt" and, if any, "publishBlockers". "publishBlockersUnavailable" means that check failed. "data" may leave required fields empty for patchDocument to fill later, except in a collection whose listCapabilities entry has "draftValidation" true or "drafts" false. A field describeSchema does not list is refused, and "id" is always assigned. The response links the document with "adminUrl" and "previewUrl".
|
|
18
19
|
|
|
19
20
|
${liveWriteSentence(scope, "create")}${uploadSentence(scope)}`;
|
|
20
21
|
/**
|
|
22
|
+
* What createDocument and duplicateDocument return: the new document, re-read
|
|
23
|
+
* privileged in `locale`, with its links and publish blockers.
|
|
24
|
+
*/ const createdResult = async (scope, args) => {
|
|
25
|
+
const { target, locale } = args;
|
|
26
|
+
const saved = await readDraft(scope, {
|
|
27
|
+
target: {
|
|
28
|
+
...target,
|
|
29
|
+
id: args.id
|
|
30
|
+
},
|
|
31
|
+
locale,
|
|
32
|
+
privileged: true
|
|
33
|
+
});
|
|
34
|
+
const validation = await collectPublishBlockers(scope.req, {
|
|
35
|
+
doc: saved,
|
|
36
|
+
entity: target
|
|
37
|
+
});
|
|
38
|
+
return jsonResult({
|
|
39
|
+
id: saved["id"],
|
|
40
|
+
status: saved["_status"],
|
|
41
|
+
updatedAt: saved["updatedAt"],
|
|
42
|
+
...await documentLinks(scope.req, {
|
|
43
|
+
target,
|
|
44
|
+
doc: saved,
|
|
45
|
+
locale
|
|
46
|
+
}),
|
|
47
|
+
...validation.blockers.length > 0 ? { publishBlockers: validation.blockers } : {},
|
|
48
|
+
...validation.unavailable ? { publishBlockersUnavailable: true } : {}
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
21
52
|
* Collection-only, because a global always exists. A create in an upload
|
|
22
53
|
* collection needs `file`: the call then returns an upload grant, and the PUT
|
|
23
54
|
* to it runs the call again with the bytes.
|
|
@@ -88,26 +119,12 @@ ${liveWriteSentence(scope, "create")}${uploadSentence(scope)}`;
|
|
|
88
119
|
...locale === void 0 ? {} : { locale },
|
|
89
120
|
...bytes === void 0 ? {} : { file: bytes }
|
|
90
121
|
});
|
|
91
|
-
|
|
92
|
-
target
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
},
|
|
96
|
-
locale,
|
|
97
|
-
privileged: true
|
|
98
|
-
});
|
|
99
|
-
const validation = await collectPublishBlockers(scope.req, {
|
|
100
|
-
doc: saved,
|
|
101
|
-
entity: target
|
|
102
|
-
});
|
|
103
|
-
return jsonResult({
|
|
104
|
-
id: saved["id"],
|
|
105
|
-
status: saved["_status"],
|
|
106
|
-
updatedAt: saved["updatedAt"],
|
|
107
|
-
...validation.blockers.length > 0 ? { publishBlockers: validation.blockers } : {},
|
|
108
|
-
...validation.unavailable ? { publishBlockersUnavailable: true } : {}
|
|
122
|
+
return await createdResult(scope, {
|
|
123
|
+
target,
|
|
124
|
+
id: created["id"],
|
|
125
|
+
locale
|
|
109
126
|
});
|
|
110
127
|
}
|
|
111
128
|
});
|
|
112
129
|
//#endregion
|
|
113
|
-
export { createDocument };
|
|
130
|
+
export { createDocument, createdResult };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { defineMcpxTool } from "../define-tool.mjs";
|
|
2
|
+
import { idSchema, liveWriteSentence, localeOf, slugEnum, slugsFor } from "./shared.mjs";
|
|
3
|
+
import { resolveEntity } from "./document.mjs";
|
|
4
|
+
import { createdResult } from "./create-document.mjs";
|
|
5
|
+
import { withDuplicateIntent } from "../write/publish-intent.mjs";
|
|
6
|
+
import { Forbidden } from "payload";
|
|
7
|
+
//#region src/tools/duplicate-document.ts
|
|
8
|
+
const duplicableSlugs = (scope) => slugsFor(scope, "create").collections.filter((slug) => scope.req.payload.config.collections.find((collection) => collection.slug === slug)?.disableDuplicate !== true && !scope.exposure.collections.some((entity) => entity.slug === slug && entity.isUpload));
|
|
9
|
+
const DESCRIPTION = (scope) => `Copies a document into a new one in the same collection. Use it instead of getDocument and createDocument to copy a document. It copies the latest version, the draft if there is one, in every locale. Unique text fields get " - Copy" appended. Returns the same fields as createDocument. To rename the copy, call patchDocument with the returned "updatedAt".
|
|
10
|
+
|
|
11
|
+
${liveWriteSentence(scope, "create")}`;
|
|
12
|
+
/**
|
|
13
|
+
* Wraps Payload's duplicate, which checks read access on the source and create
|
|
14
|
+
* access on the copy, and runs the fields' `beforeDuplicate` hooks.
|
|
15
|
+
*/ const duplicateDocument = defineMcpxTool({
|
|
16
|
+
name: "duplicateDocument",
|
|
17
|
+
description: DESCRIPTION,
|
|
18
|
+
annotations: {
|
|
19
|
+
readOnlyHint: false,
|
|
20
|
+
destructiveHint: false,
|
|
21
|
+
idempotentHint: false,
|
|
22
|
+
openWorldHint: false
|
|
23
|
+
},
|
|
24
|
+
isEnabled: (scope) => duplicableSlugs(scope).length > 0,
|
|
25
|
+
inputSchema: (scope) => ({
|
|
26
|
+
collection: slugEnum(duplicableSlugs(scope)),
|
|
27
|
+
id: idSchema
|
|
28
|
+
}),
|
|
29
|
+
handler: async ({ args, scope }) => {
|
|
30
|
+
const target = resolveEntity(scope, { collection: args.collection }, "create");
|
|
31
|
+
if (target.kind === "global" || !duplicableSlugs(scope).includes(target.slug)) throw new Forbidden(scope.req.t);
|
|
32
|
+
const created = await scope.req.payload.duplicate({
|
|
33
|
+
collection: target.slug,
|
|
34
|
+
id: args.id,
|
|
35
|
+
data: withDuplicateIntent({}),
|
|
36
|
+
depth: 0,
|
|
37
|
+
draft: true,
|
|
38
|
+
overrideAccess: false,
|
|
39
|
+
req: scope.req
|
|
40
|
+
});
|
|
41
|
+
return await createdResult(scope, {
|
|
42
|
+
target,
|
|
43
|
+
id: created["id"],
|
|
44
|
+
locale: localeOf(scope, void 0)
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
//#endregion
|
|
49
|
+
export { duplicateDocument };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { formatAdminURL } from "payload/shared";
|
|
2
|
+
//#region src/tools/links.ts
|
|
3
|
+
const livePreviewOf = (req, target) => {
|
|
4
|
+
if (target.config.admin.livePreview) return target.config.admin.livePreview;
|
|
5
|
+
const root = req.payload.config.admin.livePreview;
|
|
6
|
+
return (target.kind === "collection" ? root?.collections : root?.globals)?.includes(target.slug) ? root : void 0;
|
|
7
|
+
};
|
|
8
|
+
const previewOf = async (req, args) => {
|
|
9
|
+
const { target, doc, locale } = args;
|
|
10
|
+
const { preview } = target.config.admin;
|
|
11
|
+
if (preview) return await preview(doc, {
|
|
12
|
+
locale: locale ?? "",
|
|
13
|
+
req,
|
|
14
|
+
token: null
|
|
15
|
+
});
|
|
16
|
+
const url = livePreviewOf(req, target)?.url;
|
|
17
|
+
if (typeof url !== "function") return url;
|
|
18
|
+
const { localization } = req.payload.config;
|
|
19
|
+
const entry = localization ? localization.locales.find((item) => item.code === locale) : void 0;
|
|
20
|
+
return await url({
|
|
21
|
+
...target.kind === "collection" ? { collectionConfig: target.config } : { globalConfig: target.config },
|
|
22
|
+
data: doc,
|
|
23
|
+
locale: entry ?? {
|
|
24
|
+
code: locale ?? "",
|
|
25
|
+
label: locale ?? ""
|
|
26
|
+
},
|
|
27
|
+
payload: req.payload,
|
|
28
|
+
req
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* The admin panel URL of a written document, and its preview URL where the
|
|
33
|
+
* entity configures one. Both sit at the origin of `serverURL` when set, else
|
|
34
|
+
* of the MCP request. A preview that fails is left out: the write has landed.
|
|
35
|
+
*/ const documentLinks = async (req, args) => {
|
|
36
|
+
const { target, doc, locale } = args;
|
|
37
|
+
const { config } = req.payload;
|
|
38
|
+
const origin = config.serverURL || new URL(req.url ?? "").origin;
|
|
39
|
+
const path = target.kind === "collection" ? `/collections/${target.slug}/${String(doc["id"])}` : `/globals/${target.slug}`;
|
|
40
|
+
const adminUrl = formatAdminURL({
|
|
41
|
+
adminRoute: config.routes.admin,
|
|
42
|
+
path,
|
|
43
|
+
serverURL: origin
|
|
44
|
+
});
|
|
45
|
+
const preview = await previewOf(req, args).catch(() => void 0);
|
|
46
|
+
return {
|
|
47
|
+
adminUrl: locale ? `${adminUrl}?locale=${encodeURIComponent(locale)}` : adminUrl,
|
|
48
|
+
...preview ? { previewUrl: new URL(preview, origin).toString() } : {}
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
//#endregion
|
|
52
|
+
export { documentLinks };
|
|
@@ -7,6 +7,7 @@ import { collectPublishBlockers } from "../write/publish-blockers.mjs";
|
|
|
7
7
|
import { assertAcceptsFile, fileShape, fileSlugs, fileWriteRefusal, uploadHandoff, uploadedFile } from "../upload/file.mjs";
|
|
8
8
|
import { entityShape, idShape, liveWriteSentence, localeOf, localeShape } from "./shared.mjs";
|
|
9
9
|
import { identityOf, readDraft, resolveDocument, staleReadResult } from "./document.mjs";
|
|
10
|
+
import { documentLinks } from "./links.mjs";
|
|
10
11
|
import { withTransaction } from "../write/transaction.mjs";
|
|
11
12
|
import { applyPatchOperations, isElementPointer } from "../write/patch.mjs";
|
|
12
13
|
import { buildWriteData } from "../write/write-data.mjs";
|
|
@@ -22,7 +23,7 @@ Read the document with getDocument right before patching. Append to a list with
|
|
|
22
23
|
|
|
23
24
|
In a rich text field a pointer continues into the rich text state: "/content/root/children/2" is a node, "/content/root/children/2/tag" one of its properties and "/content/root/children/3/fields/tone" a field of a block node. getDocument "outline" lists each node's pointer and "version". Build nodes from describeSchema "nodeProperties". Check a position before writing to it, e.g. {"op":"test","path":"/content/root/children/2/type","value":"heading"}.
|
|
24
25
|
|
|
25
|
-
The response carries "updatedAt" and, if any, "publishBlockers": what must be fixed before publishing. "publishBlockersUnavailable" means that check failed. The write stands either way. "notApplied" lists pointers whose value did not change or cannot be read back, for example where field access denies the update.`;
|
|
26
|
+
The response carries "updatedAt" and, if any, "publishBlockers": what must be fixed before publishing. "publishBlockersUnavailable" means that check failed. The write stands either way. "notApplied" lists pointers whose value did not change or cannot be read back, for example where field access denies the update. The response links the document with "adminUrl" and "previewUrl".`;
|
|
26
27
|
const POINTER = z.string().regex(JSON_POINTER_PATTERN);
|
|
27
28
|
const PATCHES_LIMIT = 500;
|
|
28
29
|
/**
|
|
@@ -130,6 +131,11 @@ const notAppliedPointers = (patches, intended, saved) => patches.flatMap((operat
|
|
|
130
131
|
...identityOf(target, saved["id"]),
|
|
131
132
|
status: saved["_status"],
|
|
132
133
|
updatedAt: saved["updatedAt"],
|
|
134
|
+
...await documentLinks(scope.req, {
|
|
135
|
+
target,
|
|
136
|
+
doc: saved,
|
|
137
|
+
locale
|
|
138
|
+
}),
|
|
133
139
|
...validation.blockers.length > 0 ? { publishBlockers: validation.blockers } : {},
|
|
134
140
|
...validation.unavailable ? { publishBlockersUnavailable: true } : {},
|
|
135
141
|
...notApplied.length > 0 ? { notApplied } : {}
|
|
@@ -2,6 +2,7 @@ import { defineMcpxTool } from "../define-tool.mjs";
|
|
|
2
2
|
import { jsonResult } from "../result.mjs";
|
|
3
3
|
import { entityShape, idShape, localeOf, localeShape } from "./shared.mjs";
|
|
4
4
|
import { collectLocaleBlockers, identityOf, readDraft, resolveDocument, staleReadResult } from "./document.mjs";
|
|
5
|
+
import { documentLinks } from "./links.mjs";
|
|
5
6
|
import { withPublishIntent } from "../write/publish-intent.mjs";
|
|
6
7
|
import { withTransaction } from "../write/transaction.mjs";
|
|
7
8
|
import { z } from "zod";
|
|
@@ -10,7 +11,7 @@ import { z } from "zod";
|
|
|
10
11
|
* with drafts and the key has both the `write` and `publish` checkboxes.
|
|
11
12
|
*/ const publishDocument = defineMcpxTool({
|
|
12
13
|
name: "publishDocument",
|
|
13
|
-
description: `Makes the current draft of one document or global public, in every locale or only in "locale". A scoped publish keeps the other locales of localized fields as last published, and a never-published document goes live in full. A draft with publish blockers is refused, so call validateDocument first. Payload checks only the published locale, so "otherLocaleBlockers" lists what other locales still lack. There is no unpublish.`,
|
|
14
|
+
description: `Makes the current draft of one document or global public, in every locale or only in "locale". A scoped publish keeps the other locales of localized fields as last published, and a never-published document goes live in full. A draft with publish blockers is refused, so call validateDocument first. Payload checks only the published locale, so "otherLocaleBlockers" lists what other locales still lack. There is no unpublish. The response links the document with "adminUrl" and "previewUrl".`,
|
|
14
15
|
annotations: {
|
|
15
16
|
destructiveHint: true,
|
|
16
17
|
openWorldHint: false
|
|
@@ -63,6 +64,11 @@ import { z } from "zod";
|
|
|
63
64
|
...identityOf(target, saved["id"]),
|
|
64
65
|
status: saved["_status"],
|
|
65
66
|
updatedAt: saved["updatedAt"],
|
|
67
|
+
...await documentLinks(scope.req, {
|
|
68
|
+
target,
|
|
69
|
+
doc: saved,
|
|
70
|
+
locale
|
|
71
|
+
}),
|
|
66
72
|
...others.blockers.length > 0 ? { otherLocaleBlockers: others.blockers } : {},
|
|
67
73
|
...others.unavailable ? { otherLocaleBlockersUnavailable: true } : {}
|
|
68
74
|
});
|
package/dist/types.d.mts
CHANGED
|
@@ -34,8 +34,9 @@ export interface McpxCollectionOptions {
|
|
|
34
34
|
*/
|
|
35
35
|
read?: boolean;
|
|
36
36
|
/**
|
|
37
|
-
* Expose `patchDocument`, `validateDocument
|
|
38
|
-
* collection is created in only where MCP
|
|
37
|
+
* Expose `patchDocument`, `validateDocument`, `createDocument` and
|
|
38
|
+
* `duplicateDocument`. An upload collection is created in only where MCP
|
|
39
|
+
* accepts its files, and is never duplicated. Without drafts
|
|
39
40
|
* a write changes live content. Needs `read`. Default `true` while `read` is.
|
|
40
41
|
*/
|
|
41
42
|
write?: boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { hasPublishIntent, hasTrashIntent, takePublishIntent, takeTrashIntent, withTrashIntent } from "./publish-intent.mjs";
|
|
1
|
+
import { hasPublishIntent, hasTrashIntent, takeDuplicateIntent, takePublishIntent, takeTrashIntent, withTrashIntent } from "./publish-intent.mjs";
|
|
2
2
|
import { isMcpxRequest } from "../request.mjs";
|
|
3
3
|
import { APIError } from "payload";
|
|
4
4
|
import { hasDraftsEnabled } from "payload/shared";
|
|
@@ -30,6 +30,11 @@ const scrubWriteArgs = (args, intent) => {
|
|
|
30
30
|
const forceDraftWrite = (hookArgs) => {
|
|
31
31
|
const { args, operation, req } = hookArgs;
|
|
32
32
|
if (!isMcpxRequest(req) || operation !== "create" && operation !== "update") return args;
|
|
33
|
+
if (operation === "create" && takeDuplicateIntent(args.data)) {
|
|
34
|
+
const next = scrubWriteArgs(args, void 0);
|
|
35
|
+
next["duplicateFromID"] = args.duplicateFromID;
|
|
36
|
+
return next;
|
|
37
|
+
}
|
|
33
38
|
const intent = operation !== "update" ? void 0 : hasPublishIntent(args.data) ? "publish" : hasTrashIntent(args.data) ? "trash" : void 0;
|
|
34
39
|
return scrubWriteArgs(args, intent);
|
|
35
40
|
};
|
|
@@ -3,6 +3,7 @@ import { randomUUID } from "node:crypto";
|
|
|
3
3
|
//#region src/write/publish-intent.ts
|
|
4
4
|
const PUBLISH_INTENT = "__mcpxPublishIntent";
|
|
5
5
|
const TRASH_INTENT = "__mcpxTrashIntent";
|
|
6
|
+
const DUPLICATE_INTENT = "__mcpxDuplicateIntent";
|
|
6
7
|
const TOKEN = randomUUID();
|
|
7
8
|
const take = (data, key) => {
|
|
8
9
|
if (!isPlainObject(data) || data[key] !== TOKEN) return false;
|
|
@@ -25,5 +26,12 @@ const hasTrashIntent = (data) => isPlainObject(data) && data[TRASH_INTENT] === T
|
|
|
25
26
|
/**
|
|
26
27
|
* The counterpart of {@link takePublishIntent}.
|
|
27
28
|
*/ const takeTrashIntent = (data) => take(data, TRASH_INTENT);
|
|
29
|
+
const withDuplicateIntent = (data) => ({
|
|
30
|
+
...data,
|
|
31
|
+
[DUPLICATE_INTENT]: TOKEN
|
|
32
|
+
});
|
|
33
|
+
/**
|
|
34
|
+
* Removes the marker, so it never reaches the operation.
|
|
35
|
+
*/ const takeDuplicateIntent = (data) => take(data, DUPLICATE_INTENT);
|
|
28
36
|
//#endregion
|
|
29
|
-
export { hasPublishIntent, hasTrashIntent, takePublishIntent, takeTrashIntent, withPublishIntent, withTrashIntent };
|
|
37
|
+
export { hasPublishIntent, hasTrashIntent, takeDuplicateIntent, takePublishIntent, takeTrashIntent, withDuplicateIntent, withPublishIntent, withTrashIntent };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@abinnovision/payloadcms-mcpx",
|
|
4
|
-
"version": "2.0.0-beta.
|
|
4
|
+
"version": "2.0.0-beta.18",
|
|
5
5
|
"description": "Payload CMS plugin exposing a fixed, schema-aware MCP tool surface with draft-only writes and per-API-key capabilities.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"payload",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
},
|
|
58
58
|
"prettier": "@abinnovision/prettier-config",
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
60
|
+
"@modelcontextprotocol/sdk": "^1.31.0",
|
|
61
61
|
"rfc6902": "^5.3.0",
|
|
62
62
|
"zod": "^4.6.5"
|
|
63
63
|
},
|
|
@@ -69,20 +69,20 @@
|
|
|
69
69
|
"@payloadcms/db-sqlite": "3.90.2",
|
|
70
70
|
"@payloadcms/richtext-lexical": "3.90.2",
|
|
71
71
|
"@payloadcms/ui": "3.90.2",
|
|
72
|
-
"@swc/core": "^1.16.
|
|
73
|
-
"@types/node": "^26.
|
|
72
|
+
"@swc/core": "^1.16.12",
|
|
73
|
+
"@types/node": "^26.6.3",
|
|
74
74
|
"@types/react": "^19.3.0",
|
|
75
|
-
"@vitest/coverage-v8": "^5.0.
|
|
76
|
-
"eslint": "^10.
|
|
75
|
+
"@vitest/coverage-v8": "^5.0.2",
|
|
76
|
+
"eslint": "^10.11.0",
|
|
77
77
|
"payload": "3.90.2",
|
|
78
|
-
"prettier": "^3.9.
|
|
78
|
+
"prettier": "^3.9.9",
|
|
79
79
|
"publint": "^0.3.24",
|
|
80
80
|
"react": "^19.3.0",
|
|
81
81
|
"react-dom": "^19.3.0",
|
|
82
82
|
"tsdown": "^0.23.0",
|
|
83
83
|
"typescript": "^6.0.3",
|
|
84
|
-
"unplugin-swc": "^
|
|
85
|
-
"vitest": "^5.0.
|
|
84
|
+
"unplugin-swc": "^2.0.0",
|
|
85
|
+
"vitest": "^5.0.2"
|
|
86
86
|
},
|
|
87
87
|
"peerDependencies": {
|
|
88
88
|
"@payloadcms/ui": ">=3.88.0 <4",
|