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

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.
@@ -1,4 +1,7 @@
1
1
  import { errorResult, jsonResult } from "../result.mjs";
2
+ import { SchemaError } from "../schema/errors.mjs";
3
+ import { resolveDataPointer } from "../schema/pointer.mjs";
4
+ import "../schema/index.mjs";
2
5
  import { issueGrant, uploadKvSlug } from "./grant.mjs";
3
6
  import { collectPublishBlockers } from "../write/publish-blockers.mjs";
4
7
  import { APIError } from "payload";
@@ -77,22 +80,38 @@ const sharesPublishedFile = async (req, entity, id) => {
77
80
  })]);
78
81
  return latest?.["_status"] === "draft" && published?.["_status"] === "published" && latest["filename"] === published["filename"];
79
82
  };
83
+ const isClientWritable = (scope, target, path) => {
84
+ try {
85
+ const resolution = resolveDataPointer(scope.req.payload.config, {
86
+ ref: target.entity,
87
+ doc: target.data,
88
+ pointer: path
89
+ });
90
+ return resolution.readOnly !== true && resolution.descriptor?.readOnly !== true;
91
+ } catch (error) {
92
+ if (error instanceof SchemaError) return false;
93
+ throw error;
94
+ }
95
+ };
80
96
  /**
81
97
  * Refuses a file write Payload would turn into data loss: a replace that
82
98
  * deletes the published file, or a write that fails validation after the old
83
99
  * file is gone. Payload validates only after it moves files, so the merged
84
- * data is validated here first wherever Payload will validate it.
100
+ * data is validated here first wherever Payload will validate it, keeping only
101
+ * failures the client can fix. Once the bytes are attached, Payload validates
102
+ * with every hook and the file.
85
103
  */ const fileWriteRefusal = async (scope, target) => {
86
104
  const { entity, id } = target;
87
105
  if (id !== void 0 && await sharesPublishedFile(scope.req, entity, id)) return errorResult("The file was not replaced: the latest draft still uses the published file, which the replace would delete. Publish or discard the draft first.");
88
- if (hasDraftsEnabled(entity.config) && !hasDraftValidationEnabled(entity.config)) return;
89
- const { blockers } = await collectPublishBlockers(scope.req, {
106
+ if (uploadedFile(scope.req) !== void 0 || hasDraftsEnabled(entity.config) && !hasDraftValidationEnabled(entity.config)) return;
107
+ const { blockers: found } = await collectPublishBlockers(scope.req, {
90
108
  doc: {
91
109
  ...target.data,
92
110
  ...id === void 0 ? {} : { id }
93
111
  },
94
112
  entity
95
113
  });
114
+ const blockers = found.filter((blocker) => isClientWritable(scope, target, blocker.path));
96
115
  return blockers.length === 0 ? void 0 : errorResult("Nothing was written: the document fails validation.", { validationErrors: blockers });
97
116
  };
98
117
  const handoffUrl = (req, path) => {
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.12",
4
+ "version": "2.0.0-beta.13",
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",