@payloadcms/plugin-mcp 4.0.0-internal.cc18726 → 4.0.0-internal.cd47d20
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/dist/mcp/buildMcpServer.d.ts +1 -1
- package/dist/mcp/buildMcpServer.d.ts.map +1 -1
- package/dist/mcp/buildMcpServer.js +2 -2
- package/dist/mcp/buildMcpServer.js.map +1 -1
- package/dist/mcp/builtin/collections/authTools.d.ts.map +1 -1
- package/dist/mcp/builtin/collections/authTools.js +4 -3
- package/dist/mcp/builtin/collections/authTools.js.map +1 -1
- package/dist/mcp/builtin/collections/createTool.d.ts.map +1 -1
- package/dist/mcp/builtin/collections/createTool.js +31 -24
- package/dist/mcp/builtin/collections/createTool.js.map +1 -1
- package/dist/mcp/builtin/collections/deleteTool.js +6 -2
- package/dist/mcp/builtin/collections/deleteTool.js.map +1 -1
- package/dist/mcp/builtin/collections/duplicateTool.d.ts.map +1 -1
- package/dist/mcp/builtin/collections/duplicateTool.js +17 -4
- package/dist/mcp/builtin/collections/duplicateTool.js.map +1 -1
- package/dist/mcp/builtin/collections/fileInput.d.ts +1 -1
- package/dist/mcp/builtin/collections/fileInput.d.ts.map +1 -1
- package/dist/mcp/builtin/collections/fileInput.js +19 -54
- package/dist/mcp/builtin/collections/fileInput.js.map +1 -1
- package/dist/mcp/builtin/collections/findTool.js +7 -3
- package/dist/mcp/builtin/collections/findTool.js.map +1 -1
- package/dist/mcp/builtin/collections/updateTool.d.ts.map +1 -1
- package/dist/mcp/builtin/collections/updateTool.js +25 -15
- package/dist/mcp/builtin/collections/updateTool.js.map +1 -1
- package/dist/mcp/builtin/formatEntityError.d.ts +16 -0
- package/dist/mcp/builtin/formatEntityError.d.ts.map +1 -0
- package/dist/mcp/builtin/formatEntityError.js +55 -0
- package/dist/mcp/builtin/formatEntityError.js.map +1 -0
- package/dist/mcp/builtin/globals/updateTool.d.ts.map +1 -1
- package/dist/mcp/builtin/globals/updateTool.js +12 -16
- package/dist/mcp/builtin/globals/updateTool.js.map +1 -1
- package/dist/stdio.js +4 -3
- package/dist/stdio.js.map +1 -1
- package/package.json +4 -4
- package/src/mcp/buildMcpServer.ts +3 -3
- package/src/mcp/builtin/collections/authTools.ts +5 -2
- package/src/mcp/builtin/collections/createTool.ts +52 -31
- package/src/mcp/builtin/collections/deleteTool.ts +2 -2
- package/src/mcp/builtin/collections/duplicateTool.ts +15 -3
- package/src/mcp/builtin/collections/fileInput.ts +17 -59
- package/src/mcp/builtin/collections/findTool.ts +3 -3
- package/src/mcp/builtin/collections/updateTool.ts +14 -14
- package/src/mcp/builtin/formatEntityError.ts +77 -0
- package/src/mcp/builtin/globals/updateTool.ts +6 -11
- package/src/stdio.ts +2 -2
- package/dist/mcp/builtin/collections/fileInput.spec.js +0 -86
- package/dist/mcp/builtin/collections/fileInput.spec.js.map +0 -1
- package/dist/mcp/builtin/collections/formatCollectionError.d.ts +0 -9
- package/dist/mcp/builtin/collections/formatCollectionError.d.ts.map +0 -1
- package/dist/mcp/builtin/collections/formatCollectionError.js +0 -60
- package/dist/mcp/builtin/collections/formatCollectionError.js.map +0 -1
- package/dist/mcp/builtin/validateEntityData.d.ts +0 -14
- package/dist/mcp/builtin/validateEntityData.d.ts.map +0 -1
- package/dist/mcp/builtin/validateEntityData.js +0 -82
- package/dist/mcp/builtin/validateEntityData.js.map +0 -1
- package/src/mcp/builtin/collections/formatCollectionError.ts +0 -84
- package/src/mcp/builtin/validateEntityData.ts +0 -129
package/src/stdio.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { Config, Plugin, SanitizedConfig } from 'payload'
|
|
|
3
3
|
/* eslint-disable no-console */
|
|
4
4
|
import { serveStdio } from '@modelcontextprotocol/server/stdio'
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
6
|
-
import {
|
|
6
|
+
import { createPayloadRequest, getPayload } from 'payload'
|
|
7
7
|
import { findConfig, loadEnv } from 'payload/node'
|
|
8
8
|
|
|
9
9
|
import type { SanitizedMCPPluginConfig } from './types.js'
|
|
@@ -91,7 +91,7 @@ export const runMcpStdio = async (): Promise<void> => {
|
|
|
91
91
|
headers.set('Authorization', process.env.PAYLOAD_MCP_AUTHORIZATION)
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
const req = await
|
|
94
|
+
const req = await createPayloadRequest({ payload, req: { headers } })
|
|
95
95
|
req.payloadAPI = 'MCP' as const
|
|
96
96
|
const authorizedMCP = await getAuthorizedMCP({ overrideAccess, req })
|
|
97
97
|
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { fileInputSchema, resolveFile } from './fileInput.js';
|
|
3
|
-
describe('MCP file input', ()=>{
|
|
4
|
-
it('should reject malformed MIME types', ()=>{
|
|
5
|
-
const result = fileInputSchema.safeParse({
|
|
6
|
-
data: 'aGVsbG8=',
|
|
7
|
-
mimeType: 'text/plain\r\nx-test: value',
|
|
8
|
-
name: 'hello.txt',
|
|
9
|
-
source: 'base64'
|
|
10
|
-
});
|
|
11
|
-
expect(result.success).toBe(false);
|
|
12
|
-
});
|
|
13
|
-
it('should accept a prepared upload', ()=>{
|
|
14
|
-
const result = fileInputSchema.safeParse({
|
|
15
|
-
file: {
|
|
16
|
-
filename: 'image.png',
|
|
17
|
-
mimeType: 'image/png',
|
|
18
|
-
size: 123,
|
|
19
|
-
uploadReference: {
|
|
20
|
-
uploadId: 'upload-id'
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
source: 'uploadReference'
|
|
24
|
-
});
|
|
25
|
-
expect(result.success).toBe(true);
|
|
26
|
-
});
|
|
27
|
-
it('should reject oversized base64 files', async ()=>{
|
|
28
|
-
const req = createRequest({
|
|
29
|
-
maxFileSize: 4
|
|
30
|
-
});
|
|
31
|
-
await expect(resolveFile({
|
|
32
|
-
slug: 'media',
|
|
33
|
-
input: {
|
|
34
|
-
data: Buffer.from('hello').toString('base64'),
|
|
35
|
-
mimeType: 'text/plain',
|
|
36
|
-
name: 'hello.txt',
|
|
37
|
-
source: 'base64'
|
|
38
|
-
},
|
|
39
|
-
req
|
|
40
|
-
})).rejects.toThrow('File exceeds the 4 byte upload limit.');
|
|
41
|
-
});
|
|
42
|
-
it('should reject URLs outside the collection allowlist', async ()=>{
|
|
43
|
-
const req = createRequest({
|
|
44
|
-
allowList: [
|
|
45
|
-
{
|
|
46
|
-
hostname: 'assets.example.com',
|
|
47
|
-
protocol: 'https'
|
|
48
|
-
}
|
|
49
|
-
]
|
|
50
|
-
});
|
|
51
|
-
await expect(resolveFile({
|
|
52
|
-
slug: 'media',
|
|
53
|
-
input: {
|
|
54
|
-
source: 'externalURL',
|
|
55
|
-
url: 'https://example.com/image.png'
|
|
56
|
-
},
|
|
57
|
-
req
|
|
58
|
-
})).rejects.toThrow('The provided file URL is not allowed.');
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
function createRequest({ allowList, maxFileSize }) {
|
|
62
|
-
return {
|
|
63
|
-
payload: {
|
|
64
|
-
collections: {
|
|
65
|
-
media: {
|
|
66
|
-
config: {
|
|
67
|
-
upload: allowList ? {
|
|
68
|
-
pasteURL: {
|
|
69
|
-
allowList
|
|
70
|
-
}
|
|
71
|
-
} : {}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
|
-
config: {
|
|
76
|
-
upload: {
|
|
77
|
-
limits: {
|
|
78
|
-
fileSize: maxFileSize
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
//# sourceMappingURL=fileInput.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/mcp/builtin/collections/fileInput.spec.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nimport { describe, expect, it } from 'vitest'\n\nimport { fileInputSchema, resolveFile } from './fileInput.js'\n\ndescribe('MCP file input', () => {\n it('should reject malformed MIME types', () => {\n const result = fileInputSchema.safeParse({\n data: 'aGVsbG8=',\n mimeType: 'text/plain\\r\\nx-test: value',\n name: 'hello.txt',\n source: 'base64',\n })\n\n expect(result.success).toBe(false)\n })\n\n it('should accept a prepared upload', () => {\n const result = fileInputSchema.safeParse({\n file: {\n filename: 'image.png',\n mimeType: 'image/png',\n size: 123,\n uploadReference: { uploadId: 'upload-id' },\n },\n source: 'uploadReference',\n })\n\n expect(result.success).toBe(true)\n })\n\n it('should reject oversized base64 files', async () => {\n const req = createRequest({ maxFileSize: 4 })\n\n await expect(\n resolveFile({\n slug: 'media',\n input: {\n data: Buffer.from('hello').toString('base64'),\n mimeType: 'text/plain',\n name: 'hello.txt',\n source: 'base64',\n },\n req,\n }),\n ).rejects.toThrow('File exceeds the 4 byte upload limit.')\n })\n\n it('should reject URLs outside the collection allowlist', async () => {\n const req = createRequest({\n allowList: [{ hostname: 'assets.example.com', protocol: 'https' }],\n })\n\n await expect(\n resolveFile({\n slug: 'media',\n input: {\n source: 'externalURL',\n url: 'https://example.com/image.png',\n },\n req,\n }),\n ).rejects.toThrow('The provided file URL is not allowed.')\n })\n})\n\nfunction createRequest({\n allowList,\n maxFileSize,\n}: {\n allowList?: Array<{ hostname: string; protocol: 'http' | 'https' }>\n maxFileSize?: number\n}): PayloadRequest {\n return {\n payload: {\n collections: {\n media: {\n config: {\n upload: allowList ? { pasteURL: { allowList } } : {},\n },\n },\n },\n config: {\n upload: {\n limits: {\n fileSize: maxFileSize,\n },\n },\n },\n },\n } as unknown as PayloadRequest\n}\n"],"names":["describe","expect","it","fileInputSchema","resolveFile","result","safeParse","data","mimeType","name","source","success","toBe","file","filename","size","uploadReference","uploadId","req","createRequest","maxFileSize","slug","input","Buffer","from","toString","rejects","toThrow","allowList","hostname","protocol","url","payload","collections","media","config","upload","pasteURL","limits","fileSize"],"mappings":"AAEA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAQ;AAE7C,SAASC,eAAe,EAAEC,WAAW,QAAQ,iBAAgB;AAE7DJ,SAAS,kBAAkB;IACzBE,GAAG,sCAAsC;QACvC,MAAMG,SAASF,gBAAgBG,SAAS,CAAC;YACvCC,MAAM;YACNC,UAAU;YACVC,MAAM;YACNC,QAAQ;QACV;QAEAT,OAAOI,OAAOM,OAAO,EAAEC,IAAI,CAAC;IAC9B;IAEAV,GAAG,mCAAmC;QACpC,MAAMG,SAASF,gBAAgBG,SAAS,CAAC;YACvCO,MAAM;gBACJC,UAAU;gBACVN,UAAU;gBACVO,MAAM;gBACNC,iBAAiB;oBAAEC,UAAU;gBAAY;YAC3C;YACAP,QAAQ;QACV;QAEAT,OAAOI,OAAOM,OAAO,EAAEC,IAAI,CAAC;IAC9B;IAEAV,GAAG,wCAAwC;QACzC,MAAMgB,MAAMC,cAAc;YAAEC,aAAa;QAAE;QAE3C,MAAMnB,OACJG,YAAY;YACViB,MAAM;YACNC,OAAO;gBACLf,MAAMgB,OAAOC,IAAI,CAAC,SAASC,QAAQ,CAAC;gBACpCjB,UAAU;gBACVC,MAAM;gBACNC,QAAQ;YACV;YACAQ;QACF,IACAQ,OAAO,CAACC,OAAO,CAAC;IACpB;IAEAzB,GAAG,uDAAuD;QACxD,MAAMgB,MAAMC,cAAc;YACxBS,WAAW;gBAAC;oBAAEC,UAAU;oBAAsBC,UAAU;gBAAQ;aAAE;QACpE;QAEA,MAAM7B,OACJG,YAAY;YACViB,MAAM;YACNC,OAAO;gBACLZ,QAAQ;gBACRqB,KAAK;YACP;YACAb;QACF,IACAQ,OAAO,CAACC,OAAO,CAAC;IACpB;AACF;AAEA,SAASR,cAAc,EACrBS,SAAS,EACTR,WAAW,EAIZ;IACC,OAAO;QACLY,SAAS;YACPC,aAAa;gBACXC,OAAO;oBACLC,QAAQ;wBACNC,QAAQR,YAAY;4BAAES,UAAU;gCAAET;4BAAU;wBAAE,IAAI,CAAC;oBACrD;gBACF;YACF;YACAO,QAAQ;gBACNC,QAAQ;oBACNE,QAAQ;wBACNC,UAAUnB;oBACZ;gBACF;YACF;QACF;IACF;AACF"}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { CollectionSlug, PayloadRequest } from 'payload';
|
|
2
|
-
import type { MCPToolResponse } from '../../../types.js';
|
|
3
|
-
export declare const formatCollectionError: ({ slug, action, error, req, }: {
|
|
4
|
-
action: 'creating' | 'duplicating' | 'updating';
|
|
5
|
-
error: unknown;
|
|
6
|
-
req: PayloadRequest;
|
|
7
|
-
slug: CollectionSlug;
|
|
8
|
-
}) => MCPToolResponse;
|
|
9
|
-
//# sourceMappingURL=formatCollectionError.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"formatCollectionError.d.ts","sourceRoot":"","sources":["../../../../src/mcp/builtin/collections/formatCollectionError.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAI7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AA8BxD,eAAO,MAAM,qBAAqB,kCAK/B;IACD,MAAM,EAAE,UAAU,GAAG,aAAa,GAAG,UAAU,CAAA;IAC/C,KAAK,EAAE,OAAO,CAAA;IACd,GAAG,EAAE,cAAc,CAAA;IACnB,IAAI,EAAE,cAAc,CAAA;CACrB,KAAG,eAuCH,CAAA"}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { getCollectionInputSchema } from 'payload';
|
|
2
|
-
const getValidationErrors = (error)=>{
|
|
3
|
-
if (!error || typeof error !== 'object') {
|
|
4
|
-
return undefined;
|
|
5
|
-
}
|
|
6
|
-
const data = 'data' in error ? error.data : undefined;
|
|
7
|
-
if (!data || typeof data !== 'object' || !('errors' in data) || !Array.isArray(data.errors)) {
|
|
8
|
-
return undefined;
|
|
9
|
-
}
|
|
10
|
-
return data.errors;
|
|
11
|
-
};
|
|
12
|
-
const isSchemaError = (error, message)=>{
|
|
13
|
-
if (getValidationErrors(error)) {
|
|
14
|
-
return true;
|
|
15
|
-
}
|
|
16
|
-
const name = error && typeof error === 'object' && 'name' in error ? error.name : undefined;
|
|
17
|
-
return name === 'CastError' || name === 'ValidationError' || message.includes('Cast to ') || message.includes('validation failed');
|
|
18
|
-
};
|
|
19
|
-
export const formatCollectionError = ({ slug, action, error, req })=>{
|
|
20
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
21
|
-
const errors = getValidationErrors(error) ?? [
|
|
22
|
-
{
|
|
23
|
-
message: errorMessage
|
|
24
|
-
}
|
|
25
|
-
];
|
|
26
|
-
if (!isSchemaError(error, errorMessage)) {
|
|
27
|
-
return {
|
|
28
|
-
content: [
|
|
29
|
-
{
|
|
30
|
-
type: 'text',
|
|
31
|
-
text: `Error ${action} document in collection "${slug}": ${errorMessage}`
|
|
32
|
-
}
|
|
33
|
-
],
|
|
34
|
-
isError: true
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
const schema = getCollectionInputSchema({
|
|
38
|
-
collectionSlug: slug,
|
|
39
|
-
req
|
|
40
|
-
});
|
|
41
|
-
const schemaText = schema ? `\n\nUse this schema for data:\n\`\`\`json\n${JSON.stringify(schema)}\n\`\`\`` : '';
|
|
42
|
-
return {
|
|
43
|
-
content: [
|
|
44
|
-
{
|
|
45
|
-
type: 'text',
|
|
46
|
-
text: `Error ${action} document in collection "${slug}": ${errorMessage}${schemaText}`
|
|
47
|
-
}
|
|
48
|
-
],
|
|
49
|
-
isError: true,
|
|
50
|
-
...schema ? {
|
|
51
|
-
structuredContent: {
|
|
52
|
-
slug,
|
|
53
|
-
errors,
|
|
54
|
-
schema
|
|
55
|
-
}
|
|
56
|
-
} : {}
|
|
57
|
-
};
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
//# sourceMappingURL=formatCollectionError.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/mcp/builtin/collections/formatCollectionError.ts"],"sourcesContent":["import type { CollectionSlug, PayloadRequest } from 'payload'\n\nimport { getCollectionInputSchema } from 'payload'\n\nimport type { MCPToolResponse } from '../../../types.js'\n\nconst getValidationErrors = (error: unknown): undefined | unknown[] => {\n if (!error || typeof error !== 'object') {\n return undefined\n }\n\n const data = 'data' in error ? error.data : undefined\n if (!data || typeof data !== 'object' || !('errors' in data) || !Array.isArray(data.errors)) {\n return undefined\n }\n\n return data.errors\n}\n\nconst isSchemaError = (error: unknown, message: string): boolean => {\n if (getValidationErrors(error)) {\n return true\n }\n\n const name = error && typeof error === 'object' && 'name' in error ? error.name : undefined\n\n return (\n name === 'CastError' ||\n name === 'ValidationError' ||\n message.includes('Cast to ') ||\n message.includes('validation failed')\n )\n}\n\nexport const formatCollectionError = ({\n slug,\n action,\n error,\n req,\n}: {\n action: 'creating' | 'duplicating' | 'updating'\n error: unknown\n req: PayloadRequest\n slug: CollectionSlug\n}): MCPToolResponse => {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error'\n const errors = getValidationErrors(error) ?? [{ message: errorMessage }]\n\n if (!isSchemaError(error, errorMessage)) {\n return {\n content: [\n {\n type: 'text',\n text: `Error ${action} document in collection \"${slug}\": ${errorMessage}`,\n },\n ],\n isError: true,\n }\n }\n\n const schema = getCollectionInputSchema({ collectionSlug: slug, req })\n const schemaText = schema\n ? `\\n\\nUse this schema for data:\\n\\`\\`\\`json\\n${JSON.stringify(schema)}\\n\\`\\`\\``\n : ''\n\n return {\n content: [\n {\n type: 'text',\n text: `Error ${action} document in collection \"${slug}\": ${errorMessage}${schemaText}`,\n },\n ],\n isError: true,\n ...(schema\n ? {\n structuredContent: {\n slug,\n errors,\n schema,\n },\n }\n : {}),\n }\n}\n"],"names":["getCollectionInputSchema","getValidationErrors","error","undefined","data","Array","isArray","errors","isSchemaError","message","name","includes","formatCollectionError","slug","action","req","errorMessage","Error","content","type","text","isError","schema","collectionSlug","schemaText","JSON","stringify","structuredContent"],"mappings":"AAEA,SAASA,wBAAwB,QAAQ,UAAS;AAIlD,MAAMC,sBAAsB,CAACC;IAC3B,IAAI,CAACA,SAAS,OAAOA,UAAU,UAAU;QACvC,OAAOC;IACT;IAEA,MAAMC,OAAO,UAAUF,QAAQA,MAAME,IAAI,GAAGD;IAC5C,IAAI,CAACC,QAAQ,OAAOA,SAAS,YAAY,CAAE,CAAA,YAAYA,IAAG,KAAM,CAACC,MAAMC,OAAO,CAACF,KAAKG,MAAM,GAAG;QAC3F,OAAOJ;IACT;IAEA,OAAOC,KAAKG,MAAM;AACpB;AAEA,MAAMC,gBAAgB,CAACN,OAAgBO;IACrC,IAAIR,oBAAoBC,QAAQ;QAC9B,OAAO;IACT;IAEA,MAAMQ,OAAOR,SAAS,OAAOA,UAAU,YAAY,UAAUA,QAAQA,MAAMQ,IAAI,GAAGP;IAElF,OACEO,SAAS,eACTA,SAAS,qBACTD,QAAQE,QAAQ,CAAC,eACjBF,QAAQE,QAAQ,CAAC;AAErB;AAEA,OAAO,MAAMC,wBAAwB,CAAC,EACpCC,IAAI,EACJC,MAAM,EACNZ,KAAK,EACLa,GAAG,EAMJ;IACC,MAAMC,eAAed,iBAAiBe,QAAQf,MAAMO,OAAO,GAAG;IAC9D,MAAMF,SAASN,oBAAoBC,UAAU;QAAC;YAAEO,SAASO;QAAa;KAAE;IAExE,IAAI,CAACR,cAAcN,OAAOc,eAAe;QACvC,OAAO;YACLE,SAAS;gBACP;oBACEC,MAAM;oBACNC,MAAM,CAAC,MAAM,EAAEN,OAAO,yBAAyB,EAAED,KAAK,GAAG,EAAEG,cAAc;gBAC3E;aACD;YACDK,SAAS;QACX;IACF;IAEA,MAAMC,SAAStB,yBAAyB;QAAEuB,gBAAgBV;QAAME;IAAI;IACpE,MAAMS,aAAaF,SACf,CAAC,2CAA2C,EAAEG,KAAKC,SAAS,CAACJ,QAAQ,QAAQ,CAAC,GAC9E;IAEJ,OAAO;QACLJ,SAAS;YACP;gBACEC,MAAM;gBACNC,MAAM,CAAC,MAAM,EAAEN,OAAO,yBAAyB,EAAED,KAAK,GAAG,EAAEG,eAAeQ,YAAY;YACxF;SACD;QACDH,SAAS;QACT,GAAIC,SACA;YACEK,mBAAmB;gBACjBd;gBACAN;gBACAe;YACF;QACF,IACA,CAAC,CAAC;IACR;AACF,EAAC"}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { CollectionSlug, GlobalSlug, PayloadRequest } from 'payload';
|
|
2
|
-
import type { MCPToolResponse } from '../../types.js';
|
|
3
|
-
export declare const validateCollectionData: ({ slug, data, partial, req, }: {
|
|
4
|
-
data: Record<string, unknown>;
|
|
5
|
-
partial?: boolean;
|
|
6
|
-
req: PayloadRequest;
|
|
7
|
-
slug: CollectionSlug;
|
|
8
|
-
}) => MCPToolResponse | null;
|
|
9
|
-
export declare const validateGlobalData: ({ slug, data, req, }: {
|
|
10
|
-
data: Record<string, unknown>;
|
|
11
|
-
req: PayloadRequest;
|
|
12
|
-
slug: GlobalSlug;
|
|
13
|
-
}) => MCPToolResponse | null;
|
|
14
|
-
//# sourceMappingURL=validateEntityData.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"validateEntityData.d.ts","sourceRoot":"","sources":["../../../src/mcp/builtin/validateEntityData.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,cAAc,EAAmB,MAAM,SAAS,CAAA;AAK1F,OAAO,KAAK,EAAkB,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAkBrE,eAAO,MAAM,sBAAsB,kCAKhC;IACD,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,GAAG,EAAE,cAAc,CAAA;IACnB,IAAI,EAAE,cAAc,CAAA;CACrB,KAAG,eAAe,GAAG,IASlB,CAAA;AAEJ,eAAO,MAAM,kBAAkB,yBAI5B;IACD,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,GAAG,EAAE,cAAc,CAAA;IACnB,IAAI,EAAE,UAAU,CAAA;CACjB,KAAG,eAAe,GAAG,IAQlB,CAAA"}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/server/validators/cf-worker';
|
|
2
|
-
import { getCollectionInputSchema, getGlobalInputSchema } from 'payload';
|
|
3
|
-
/**
|
|
4
|
-
* Keep the default `shortcircuit: true` - `false` makes validating nested rich text exponentially slow.
|
|
5
|
-
*/ const validator = new CfWorkerJsonSchemaValidator({
|
|
6
|
-
shortcircuit: true
|
|
7
|
-
});
|
|
8
|
-
/**
|
|
9
|
-
* - Caches each entity's schema and validator, so both are only built on the first request.
|
|
10
|
-
* - Keyed by payload config, then by entity type, slug, partial and language.
|
|
11
|
-
*/ const validatorCache = new WeakMap();
|
|
12
|
-
export const validateCollectionData = ({ slug, data, partial, req })=>validateData({
|
|
13
|
-
slug,
|
|
14
|
-
buildSchema: ()=>getCollectionInputSchema({
|
|
15
|
-
collectionSlug: slug,
|
|
16
|
-
req
|
|
17
|
-
}),
|
|
18
|
-
data,
|
|
19
|
-
entity: 'collection',
|
|
20
|
-
partial,
|
|
21
|
-
req
|
|
22
|
-
});
|
|
23
|
-
export const validateGlobalData = ({ slug, data, req })=>validateData({
|
|
24
|
-
slug,
|
|
25
|
-
buildSchema: ()=>getGlobalInputSchema({
|
|
26
|
-
globalSlug: slug,
|
|
27
|
-
req
|
|
28
|
-
}),
|
|
29
|
-
data,
|
|
30
|
-
entity: 'global',
|
|
31
|
-
partial: true,
|
|
32
|
-
req
|
|
33
|
-
});
|
|
34
|
-
const validateData = ({ slug, buildSchema, data, entity, partial, req })=>{
|
|
35
|
-
let cache = validatorCache.get(req.payload.config);
|
|
36
|
-
if (!cache) {
|
|
37
|
-
cache = new Map();
|
|
38
|
-
validatorCache.set(req.payload.config, cache);
|
|
39
|
-
}
|
|
40
|
-
const cacheKey = `${entity}:${slug}:${partial ? 'partial' : 'full'}:${req.i18n.language}`;
|
|
41
|
-
let entityValidator = cache.get(cacheKey);
|
|
42
|
-
if (!entityValidator) {
|
|
43
|
-
const schema = buildSchema();
|
|
44
|
-
if (!schema) {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
entityValidator = {
|
|
48
|
-
schema,
|
|
49
|
-
validate: validator.getValidator(partial ? withoutRequired(schema) : schema)
|
|
50
|
-
};
|
|
51
|
-
cache.set(cacheKey, entityValidator);
|
|
52
|
-
}
|
|
53
|
-
const result = entityValidator.validate(data);
|
|
54
|
-
if (result.valid) {
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
const { schema } = entityValidator;
|
|
58
|
-
return {
|
|
59
|
-
content: [
|
|
60
|
-
{
|
|
61
|
-
type: 'text',
|
|
62
|
-
text: `Invalid data for ${entity} "${slug}": ${result.errorMessage}\n\nUse this schema for data:\n\`\`\`json\n${JSON.stringify(schema)}\n\`\`\``
|
|
63
|
-
}
|
|
64
|
-
],
|
|
65
|
-
isError: true,
|
|
66
|
-
structuredContent: {
|
|
67
|
-
[`${entity}Slug`]: slug,
|
|
68
|
-
errors: [
|
|
69
|
-
{
|
|
70
|
-
message: result.errorMessage
|
|
71
|
-
}
|
|
72
|
-
],
|
|
73
|
-
schema
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
};
|
|
77
|
-
/** Updates are partial — drop the schema's top-level `required` before validating. */ const withoutRequired = (schema)=>{
|
|
78
|
-
const { required: _required, ...rest } = schema;
|
|
79
|
-
return rest;
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
//# sourceMappingURL=validateEntityData.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/mcp/builtin/validateEntityData.ts"],"sourcesContent":["import type { CollectionSlug, GlobalSlug, PayloadRequest, SanitizedConfig } from 'payload'\n\nimport { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/server/validators/cf-worker'\nimport { getCollectionInputSchema, getGlobalInputSchema } from 'payload'\n\nimport type { JsonSchemaType, MCPToolResponse } from '../../types.js'\n\n/**\n * Keep the default `shortcircuit: true` - `false` makes validating nested rich text exponentially slow.\n */\nconst validator = new CfWorkerJsonSchemaValidator({ shortcircuit: true })\n\ntype EntityValidator = {\n schema: JsonSchemaType\n validate: ReturnType<(typeof validator)['getValidator']>\n}\n\n/**\n * - Caches each entity's schema and validator, so both are only built on the first request.\n * - Keyed by payload config, then by entity type, slug, partial and language.\n */\nconst validatorCache = new WeakMap<SanitizedConfig, Map<string, EntityValidator>>()\n\nexport const validateCollectionData = ({\n slug,\n data,\n partial,\n req,\n}: {\n data: Record<string, unknown>\n partial?: boolean\n req: PayloadRequest\n slug: CollectionSlug\n}): MCPToolResponse | null =>\n validateData({\n slug,\n buildSchema: () =>\n getCollectionInputSchema({ collectionSlug: slug, req }) as JsonSchemaType | null,\n data,\n entity: 'collection',\n partial,\n req,\n })\n\nexport const validateGlobalData = ({\n slug,\n data,\n req,\n}: {\n data: Record<string, unknown>\n req: PayloadRequest\n slug: GlobalSlug\n}): MCPToolResponse | null =>\n validateData({\n slug,\n buildSchema: () => getGlobalInputSchema({ globalSlug: slug, req }) as JsonSchemaType | null,\n data,\n entity: 'global',\n partial: true,\n req,\n })\n\nconst validateData = ({\n slug,\n buildSchema,\n data,\n entity,\n partial,\n req,\n}: {\n buildSchema: () => JsonSchemaType | null\n data: Record<string, unknown>\n entity: 'collection' | 'global'\n partial?: boolean\n req: PayloadRequest\n slug: string\n}): MCPToolResponse | null => {\n let cache = validatorCache.get(req.payload.config)\n if (!cache) {\n cache = new Map()\n validatorCache.set(req.payload.config, cache)\n }\n\n const cacheKey = `${entity}:${slug}:${partial ? 'partial' : 'full'}:${req.i18n.language}`\n let entityValidator = cache.get(cacheKey)\n\n if (!entityValidator) {\n const schema = buildSchema()\n\n if (!schema) {\n return null\n }\n\n entityValidator = {\n schema,\n validate: validator.getValidator(partial ? withoutRequired(schema) : schema),\n }\n cache.set(cacheKey, entityValidator)\n }\n\n const result = entityValidator.validate(data)\n\n if (result.valid) {\n return null\n }\n\n const { schema } = entityValidator\n\n return {\n content: [\n {\n type: 'text',\n text: `Invalid data for ${entity} \"${slug}\": ${result.errorMessage}\\n\\nUse this schema for data:\\n\\`\\`\\`json\\n${JSON.stringify(schema)}\\n\\`\\`\\``,\n },\n ],\n isError: true,\n structuredContent: {\n [`${entity}Slug`]: slug,\n errors: [{ message: result.errorMessage }],\n schema,\n },\n }\n}\n\n/** Updates are partial — drop the schema's top-level `required` before validating. */\nconst withoutRequired = (schema: JsonSchemaType): JsonSchemaType => {\n const { required: _required, ...rest } = schema\n return rest\n}\n"],"names":["CfWorkerJsonSchemaValidator","getCollectionInputSchema","getGlobalInputSchema","validator","shortcircuit","validatorCache","WeakMap","validateCollectionData","slug","data","partial","req","validateData","buildSchema","collectionSlug","entity","validateGlobalData","globalSlug","cache","get","payload","config","Map","set","cacheKey","i18n","language","entityValidator","schema","validate","getValidator","withoutRequired","result","valid","content","type","text","errorMessage","JSON","stringify","isError","structuredContent","errors","message","required","_required","rest"],"mappings":"AAEA,SAASA,2BAA2B,QAAQ,oDAAmD;AAC/F,SAASC,wBAAwB,EAAEC,oBAAoB,QAAQ,UAAS;AAIxE;;CAEC,GACD,MAAMC,YAAY,IAAIH,4BAA4B;IAAEI,cAAc;AAAK;AAOvE;;;CAGC,GACD,MAAMC,iBAAiB,IAAIC;AAE3B,OAAO,MAAMC,yBAAyB,CAAC,EACrCC,IAAI,EACJC,IAAI,EACJC,OAAO,EACPC,GAAG,EAMJ,GACCC,aAAa;QACXJ;QACAK,aAAa,IACXZ,yBAAyB;gBAAEa,gBAAgBN;gBAAMG;YAAI;QACvDF;QACAM,QAAQ;QACRL;QACAC;IACF,GAAE;AAEJ,OAAO,MAAMK,qBAAqB,CAAC,EACjCR,IAAI,EACJC,IAAI,EACJE,GAAG,EAKJ,GACCC,aAAa;QACXJ;QACAK,aAAa,IAAMX,qBAAqB;gBAAEe,YAAYT;gBAAMG;YAAI;QAChEF;QACAM,QAAQ;QACRL,SAAS;QACTC;IACF,GAAE;AAEJ,MAAMC,eAAe,CAAC,EACpBJ,IAAI,EACJK,WAAW,EACXJ,IAAI,EACJM,MAAM,EACNL,OAAO,EACPC,GAAG,EAQJ;IACC,IAAIO,QAAQb,eAAec,GAAG,CAACR,IAAIS,OAAO,CAACC,MAAM;IACjD,IAAI,CAACH,OAAO;QACVA,QAAQ,IAAII;QACZjB,eAAekB,GAAG,CAACZ,IAAIS,OAAO,CAACC,MAAM,EAAEH;IACzC;IAEA,MAAMM,WAAW,GAAGT,OAAO,CAAC,EAAEP,KAAK,CAAC,EAAEE,UAAU,YAAY,OAAO,CAAC,EAAEC,IAAIc,IAAI,CAACC,QAAQ,EAAE;IACzF,IAAIC,kBAAkBT,MAAMC,GAAG,CAACK;IAEhC,IAAI,CAACG,iBAAiB;QACpB,MAAMC,SAASf;QAEf,IAAI,CAACe,QAAQ;YACX,OAAO;QACT;QAEAD,kBAAkB;YAChBC;YACAC,UAAU1B,UAAU2B,YAAY,CAACpB,UAAUqB,gBAAgBH,UAAUA;QACvE;QACAV,MAAMK,GAAG,CAACC,UAAUG;IACtB;IAEA,MAAMK,SAASL,gBAAgBE,QAAQ,CAACpB;IAExC,IAAIuB,OAAOC,KAAK,EAAE;QAChB,OAAO;IACT;IAEA,MAAM,EAAEL,MAAM,EAAE,GAAGD;IAEnB,OAAO;QACLO,SAAS;YACP;gBACEC,MAAM;gBACNC,MAAM,CAAC,iBAAiB,EAAErB,OAAO,EAAE,EAAEP,KAAK,GAAG,EAAEwB,OAAOK,YAAY,CAAC,2CAA2C,EAAEC,KAAKC,SAAS,CAACX,QAAQ,QAAQ,CAAC;YAClJ;SACD;QACDY,SAAS;QACTC,mBAAmB;YACjB,CAAC,GAAG1B,OAAO,IAAI,CAAC,CAAC,EAAEP;YACnBkC,QAAQ;gBAAC;oBAAEC,SAASX,OAAOK,YAAY;gBAAC;aAAE;YAC1CT;QACF;IACF;AACF;AAEA,oFAAoF,GACpF,MAAMG,kBAAkB,CAACH;IACvB,MAAM,EAAEgB,UAAUC,SAAS,EAAE,GAAGC,MAAM,GAAGlB;IACzC,OAAOkB;AACT"}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import type { CollectionSlug, PayloadRequest } from 'payload'
|
|
2
|
-
|
|
3
|
-
import { getCollectionInputSchema } from 'payload'
|
|
4
|
-
|
|
5
|
-
import type { MCPToolResponse } from '../../../types.js'
|
|
6
|
-
|
|
7
|
-
const getValidationErrors = (error: unknown): undefined | unknown[] => {
|
|
8
|
-
if (!error || typeof error !== 'object') {
|
|
9
|
-
return undefined
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const data = 'data' in error ? error.data : undefined
|
|
13
|
-
if (!data || typeof data !== 'object' || !('errors' in data) || !Array.isArray(data.errors)) {
|
|
14
|
-
return undefined
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return data.errors
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const isSchemaError = (error: unknown, message: string): boolean => {
|
|
21
|
-
if (getValidationErrors(error)) {
|
|
22
|
-
return true
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const name = error && typeof error === 'object' && 'name' in error ? error.name : undefined
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
name === 'CastError' ||
|
|
29
|
-
name === 'ValidationError' ||
|
|
30
|
-
message.includes('Cast to ') ||
|
|
31
|
-
message.includes('validation failed')
|
|
32
|
-
)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export const formatCollectionError = ({
|
|
36
|
-
slug,
|
|
37
|
-
action,
|
|
38
|
-
error,
|
|
39
|
-
req,
|
|
40
|
-
}: {
|
|
41
|
-
action: 'creating' | 'duplicating' | 'updating'
|
|
42
|
-
error: unknown
|
|
43
|
-
req: PayloadRequest
|
|
44
|
-
slug: CollectionSlug
|
|
45
|
-
}): MCPToolResponse => {
|
|
46
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
|
|
47
|
-
const errors = getValidationErrors(error) ?? [{ message: errorMessage }]
|
|
48
|
-
|
|
49
|
-
if (!isSchemaError(error, errorMessage)) {
|
|
50
|
-
return {
|
|
51
|
-
content: [
|
|
52
|
-
{
|
|
53
|
-
type: 'text',
|
|
54
|
-
text: `Error ${action} document in collection "${slug}": ${errorMessage}`,
|
|
55
|
-
},
|
|
56
|
-
],
|
|
57
|
-
isError: true,
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const schema = getCollectionInputSchema({ collectionSlug: slug, req })
|
|
62
|
-
const schemaText = schema
|
|
63
|
-
? `\n\nUse this schema for data:\n\`\`\`json\n${JSON.stringify(schema)}\n\`\`\``
|
|
64
|
-
: ''
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
content: [
|
|
68
|
-
{
|
|
69
|
-
type: 'text',
|
|
70
|
-
text: `Error ${action} document in collection "${slug}": ${errorMessage}${schemaText}`,
|
|
71
|
-
},
|
|
72
|
-
],
|
|
73
|
-
isError: true,
|
|
74
|
-
...(schema
|
|
75
|
-
? {
|
|
76
|
-
structuredContent: {
|
|
77
|
-
slug,
|
|
78
|
-
errors,
|
|
79
|
-
schema,
|
|
80
|
-
},
|
|
81
|
-
}
|
|
82
|
-
: {}),
|
|
83
|
-
}
|
|
84
|
-
}
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import type { CollectionSlug, GlobalSlug, PayloadRequest, SanitizedConfig } from 'payload'
|
|
2
|
-
|
|
3
|
-
import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/server/validators/cf-worker'
|
|
4
|
-
import { getCollectionInputSchema, getGlobalInputSchema } from 'payload'
|
|
5
|
-
|
|
6
|
-
import type { JsonSchemaType, MCPToolResponse } from '../../types.js'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Keep the default `shortcircuit: true` - `false` makes validating nested rich text exponentially slow.
|
|
10
|
-
*/
|
|
11
|
-
const validator = new CfWorkerJsonSchemaValidator({ shortcircuit: true })
|
|
12
|
-
|
|
13
|
-
type EntityValidator = {
|
|
14
|
-
schema: JsonSchemaType
|
|
15
|
-
validate: ReturnType<(typeof validator)['getValidator']>
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* - Caches each entity's schema and validator, so both are only built on the first request.
|
|
20
|
-
* - Keyed by payload config, then by entity type, slug, partial and language.
|
|
21
|
-
*/
|
|
22
|
-
const validatorCache = new WeakMap<SanitizedConfig, Map<string, EntityValidator>>()
|
|
23
|
-
|
|
24
|
-
export const validateCollectionData = ({
|
|
25
|
-
slug,
|
|
26
|
-
data,
|
|
27
|
-
partial,
|
|
28
|
-
req,
|
|
29
|
-
}: {
|
|
30
|
-
data: Record<string, unknown>
|
|
31
|
-
partial?: boolean
|
|
32
|
-
req: PayloadRequest
|
|
33
|
-
slug: CollectionSlug
|
|
34
|
-
}): MCPToolResponse | null =>
|
|
35
|
-
validateData({
|
|
36
|
-
slug,
|
|
37
|
-
buildSchema: () =>
|
|
38
|
-
getCollectionInputSchema({ collectionSlug: slug, req }) as JsonSchemaType | null,
|
|
39
|
-
data,
|
|
40
|
-
entity: 'collection',
|
|
41
|
-
partial,
|
|
42
|
-
req,
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
export const validateGlobalData = ({
|
|
46
|
-
slug,
|
|
47
|
-
data,
|
|
48
|
-
req,
|
|
49
|
-
}: {
|
|
50
|
-
data: Record<string, unknown>
|
|
51
|
-
req: PayloadRequest
|
|
52
|
-
slug: GlobalSlug
|
|
53
|
-
}): MCPToolResponse | null =>
|
|
54
|
-
validateData({
|
|
55
|
-
slug,
|
|
56
|
-
buildSchema: () => getGlobalInputSchema({ globalSlug: slug, req }) as JsonSchemaType | null,
|
|
57
|
-
data,
|
|
58
|
-
entity: 'global',
|
|
59
|
-
partial: true,
|
|
60
|
-
req,
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
const validateData = ({
|
|
64
|
-
slug,
|
|
65
|
-
buildSchema,
|
|
66
|
-
data,
|
|
67
|
-
entity,
|
|
68
|
-
partial,
|
|
69
|
-
req,
|
|
70
|
-
}: {
|
|
71
|
-
buildSchema: () => JsonSchemaType | null
|
|
72
|
-
data: Record<string, unknown>
|
|
73
|
-
entity: 'collection' | 'global'
|
|
74
|
-
partial?: boolean
|
|
75
|
-
req: PayloadRequest
|
|
76
|
-
slug: string
|
|
77
|
-
}): MCPToolResponse | null => {
|
|
78
|
-
let cache = validatorCache.get(req.payload.config)
|
|
79
|
-
if (!cache) {
|
|
80
|
-
cache = new Map()
|
|
81
|
-
validatorCache.set(req.payload.config, cache)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const cacheKey = `${entity}:${slug}:${partial ? 'partial' : 'full'}:${req.i18n.language}`
|
|
85
|
-
let entityValidator = cache.get(cacheKey)
|
|
86
|
-
|
|
87
|
-
if (!entityValidator) {
|
|
88
|
-
const schema = buildSchema()
|
|
89
|
-
|
|
90
|
-
if (!schema) {
|
|
91
|
-
return null
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
entityValidator = {
|
|
95
|
-
schema,
|
|
96
|
-
validate: validator.getValidator(partial ? withoutRequired(schema) : schema),
|
|
97
|
-
}
|
|
98
|
-
cache.set(cacheKey, entityValidator)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const result = entityValidator.validate(data)
|
|
102
|
-
|
|
103
|
-
if (result.valid) {
|
|
104
|
-
return null
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const { schema } = entityValidator
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
content: [
|
|
111
|
-
{
|
|
112
|
-
type: 'text',
|
|
113
|
-
text: `Invalid data for ${entity} "${slug}": ${result.errorMessage}\n\nUse this schema for data:\n\`\`\`json\n${JSON.stringify(schema)}\n\`\`\``,
|
|
114
|
-
},
|
|
115
|
-
],
|
|
116
|
-
isError: true,
|
|
117
|
-
structuredContent: {
|
|
118
|
-
[`${entity}Slug`]: slug,
|
|
119
|
-
errors: [{ message: result.errorMessage }],
|
|
120
|
-
schema,
|
|
121
|
-
},
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/** Updates are partial — drop the schema's top-level `required` before validating. */
|
|
126
|
-
const withoutRequired = (schema: JsonSchemaType): JsonSchemaType => {
|
|
127
|
-
const { required: _required, ...rest } = schema
|
|
128
|
-
return rest
|
|
129
|
-
}
|