@abinnovision/payloadcms-mcpx 2.0.0-beta.13 → 2.0.0-beta.15

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 (43) hide show
  1. package/README.md +50 -35
  2. package/dist/admin/capability-matrix.d.mts +4 -3
  3. package/dist/admin/capability-matrix.mjs +262 -111
  4. package/dist/admin/capability-toggles.mjs +84 -40
  5. package/dist/admin/confirmations-panel.mjs +242 -0
  6. package/dist/admin/confirmations.d.mts +15 -0
  7. package/dist/admin/confirmations.mjs +70 -0
  8. package/dist/admin/index.d.mts +2 -1
  9. package/dist/admin/index.mjs +2 -1
  10. package/dist/api-keys/capability-matrix.d.mts +5 -4
  11. package/dist/api-keys/capability-matrix.mjs +33 -12
  12. package/dist/api-keys/collection.mjs +3 -3
  13. package/dist/api-keys/confirmation-view.mjs +6 -0
  14. package/dist/api-keys/fields.mjs +20 -3
  15. package/dist/builtin-tool-names.mjs +3 -1
  16. package/dist/capabilities.mjs +9 -4
  17. package/dist/define-tool.d.mts +4 -4
  18. package/dist/define-tool.mjs +8 -1
  19. package/dist/endpoint/confirmations.mjs +124 -0
  20. package/dist/endpoint/handler.mjs +2 -2
  21. package/dist/endpoint/server.mjs +22 -18
  22. package/dist/endpoint/upload.mjs +5 -4
  23. package/dist/grants/confirmation.mjs +106 -0
  24. package/dist/{upload → grants}/grant.mjs +39 -16
  25. package/dist/options.mjs +32 -6
  26. package/dist/plugin.d.mts +2 -1
  27. package/dist/plugin.mjs +5 -2
  28. package/dist/tools/builtin.mjs +5 -1
  29. package/dist/tools/create-document.mjs +1 -1
  30. package/dist/tools/delete-document.mjs +157 -0
  31. package/dist/tools/find-versions.mjs +1 -1
  32. package/dist/tools/get-document.mjs +1 -1
  33. package/dist/tools/list-capabilities.mjs +2 -2
  34. package/dist/tools/patch-document.mjs +2 -2
  35. package/dist/tools/publish-document.mjs +1 -1
  36. package/dist/tools/run-confirmed.mjs +126 -0
  37. package/dist/tools/shared.mjs +4 -0
  38. package/dist/tools/validate-document.mjs +2 -2
  39. package/dist/types.d.mts +130 -19
  40. package/dist/upload/file.mjs +3 -3
  41. package/dist/write/draft-guard.mjs +11 -9
  42. package/dist/write/publish-intent.mjs +16 -6
  43. package/package.json +1 -1
@@ -1,12 +1,12 @@
1
+ import { grantKvSlug, issueGrant } from "../grants/grant.mjs";
1
2
  import { errorResult, jsonResult } from "../result.mjs";
2
3
  import { SchemaError } from "../schema/errors.mjs";
3
4
  import { resolveDataPointer } from "../schema/pointer.mjs";
4
5
  import "../schema/index.mjs";
5
- import { issueGrant, uploadKvSlug } from "./grant.mjs";
6
6
  import { collectPublishBlockers } from "../write/publish-blockers.mjs";
7
+ import { z } from "zod";
7
8
  import { APIError } from "payload";
8
9
  import { hasDraftValidationEnabled, hasDraftsEnabled, validateMimeType } from "payload/shared";
9
- import { z } from "zod";
10
10
  //#region src/upload/file.ts
11
11
  const MAX_BYTES = 26214400;
12
12
  const FILENAME = /^[^/\\\p{Cc}]+$/u;
@@ -121,7 +121,7 @@ const handoffUrl = (req, path) => {
121
121
  return url.toString();
122
122
  };
123
123
  const grantContext = (req) => {
124
- const slug = uploadKvSlug(req.payload.config);
124
+ const slug = grantKvSlug(req.payload.config);
125
125
  const apiKeyId = req.context.mcpx?.apiKeyId;
126
126
  if (slug === void 0 || apiKeyId === void 0) throw new Error("A handoff was offered without a database KV or key.");
127
127
  return {
@@ -1,4 +1,4 @@
1
- import { hasPublishIntent, takePublishIntent } from "./publish-intent.mjs";
1
+ import { hasPublishIntent, hasTrashIntent, 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";
@@ -11,16 +11,17 @@ const STRIPPED_ARGS = /* @__PURE__ */ new Set([
11
11
  "selectedLocales",
12
12
  "overwriteExistingFiles"
13
13
  ]);
14
- const scrubWriteArgs = (args, publishing) => {
14
+ const scrubWriteArgs = (args, intent) => {
15
+ const publishing = intent === "publish";
15
16
  const next = Object.fromEntries(Object.entries(args).filter(([key]) => !STRIPPED_ARGS.has(key) && (publishing || key !== "publishSpecificLocale")));
16
17
  if (next["data"] && typeof next["data"] === "object") {
17
- const { _status: _ignoredStatus, deletedAt: _ignoredDeletedAt, ...data } = next["data"];
18
+ const { _status: _ignoredStatus, deletedAt, ...data } = next["data"];
18
19
  next["data"] = publishing ? {
19
20
  ...data,
20
21
  _status: "published"
21
- } : data;
22
+ } : intent === "trash" ? withTrashIntent({ deletedAt }) : data;
22
23
  }
23
- next["draft"] = !publishing;
24
+ next["draft"] = intent === void 0;
24
25
  next["autosave"] = false;
25
26
  next["overrideLock"] = false;
26
27
  next["trash"] = false;
@@ -29,8 +30,8 @@ const scrubWriteArgs = (args, publishing) => {
29
30
  const forceDraftWrite = (hookArgs) => {
30
31
  const { args, operation, req } = hookArgs;
31
32
  if (!isMcpxRequest(req) || operation !== "create" && operation !== "update") return args;
32
- const publishing = operation === "update" && hasPublishIntent(args.data);
33
- return scrubWriteArgs(args, publishing);
33
+ const intent = operation !== "update" ? void 0 : hasPublishIntent(args.data) ? "publish" : hasTrashIntent(args.data) ? "trash" : void 0;
34
+ return scrubWriteArgs(args, intent);
34
35
  };
35
36
  /**
36
37
  * The global counterpart of {@link forceDraftWrite}. `updateGlobal` reads
@@ -46,11 +47,12 @@ const forceDraftWrite = (hookArgs) => {
46
47
  req.payload.logger.warn(`[payloadcms-mcpx] Refused a write to ${global.slug} that did not ask for a draft save.`);
47
48
  throw new APIError("MCP clients may only write drafts. This write was refused because it would not have been saved as one. Use publishDocument to publish.", 403);
48
49
  }
49
- return scrubWriteArgs(args, publishing);
50
+ return scrubWriteArgs(args, publishing ? "publish" : void 0);
50
51
  };
51
52
  const refuseUnlessExpected = (req, slug, data) => {
52
53
  const publishing = takePublishIntent(data);
53
- if (!isMcpxRequest(req)) return;
54
+ const trashing = takeTrashIntent(data);
55
+ if (!isMcpxRequest(req) || trashing) return;
54
56
  const status = data._status;
55
57
  const expected = publishing ? "published" : "draft";
56
58
  if (status === expected) return;
@@ -2,7 +2,13 @@ import { isPlainObject } from "../guards.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  //#region src/write/publish-intent.ts
4
4
  const PUBLISH_INTENT = "__mcpxPublishIntent";
5
+ const TRASH_INTENT = "__mcpxTrashIntent";
5
6
  const TOKEN = randomUUID();
7
+ const take = (data, key) => {
8
+ if (!isPlainObject(data) || data[key] !== TOKEN) return false;
9
+ delete data[key];
10
+ return true;
11
+ };
6
12
  const withPublishIntent = (data) => ({
7
13
  ...data,
8
14
  [PUBLISH_INTENT]: TOKEN
@@ -10,10 +16,14 @@ const withPublishIntent = (data) => ({
10
16
  const hasPublishIntent = (data) => isPlainObject(data) && data[PUBLISH_INTENT] === TOKEN;
11
17
  /**
12
18
  * Called by the last hook that needs the marker, which removes it.
13
- */ const takePublishIntent = (data) => {
14
- if (!hasPublishIntent(data)) return false;
15
- delete data[PUBLISH_INTENT];
16
- return true;
17
- };
19
+ */ const takePublishIntent = (data) => take(data, PUBLISH_INTENT);
20
+ const withTrashIntent = (data) => ({
21
+ ...data,
22
+ [TRASH_INTENT]: TOKEN
23
+ });
24
+ const hasTrashIntent = (data) => isPlainObject(data) && data[TRASH_INTENT] === TOKEN;
25
+ /**
26
+ * The counterpart of {@link takePublishIntent}.
27
+ */ const takeTrashIntent = (data) => take(data, TRASH_INTENT);
18
28
  //#endregion
19
- export { hasPublishIntent, takePublishIntent, withPublishIntent };
29
+ export { hasPublishIntent, hasTrashIntent, takePublishIntent, takeTrashIntent, 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.13",
4
+ "version": "2.0.0-beta.15",
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",