payload-mcp-toolkit 0.3.3 → 0.7.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/README.md +232 -150
- package/dist/__tests__/api-keys.test.js +292 -0
- package/dist/__tests__/api-keys.test.js.map +1 -0
- package/dist/__tests__/auth-strategy.test.js +681 -0
- package/dist/__tests__/auth-strategy.test.js.map +1 -0
- package/dist/__tests__/conflict-detection.test.js +69 -0
- package/dist/__tests__/conflict-detection.test.js.map +1 -0
- package/dist/__tests__/delete-document.test.js +70 -0
- package/dist/__tests__/delete-document.test.js.map +1 -0
- package/dist/__tests__/endpoint.test.js +143 -0
- package/dist/__tests__/endpoint.test.js.map +1 -0
- package/dist/__tests__/find-document.test.js +178 -0
- package/dist/__tests__/find-document.test.js.map +1 -0
- package/dist/__tests__/find-global.test.js +173 -0
- package/dist/__tests__/find-global.test.js.map +1 -0
- package/dist/__tests__/global-versions.test.js +183 -0
- package/dist/__tests__/global-versions.test.js.map +1 -0
- package/dist/__tests__/hash.test.js +58 -0
- package/dist/__tests__/hash.test.js.map +1 -0
- package/dist/__tests__/index-integration.test.js +191 -0
- package/dist/__tests__/index-integration.test.js.map +1 -0
- package/dist/__tests__/introspection.test.js +201 -1
- package/dist/__tests__/introspection.test.js.map +1 -1
- package/dist/__tests__/patch-global-layout.test.js +474 -0
- package/dist/__tests__/patch-global-layout.test.js.map +1 -0
- package/dist/__tests__/patch-layout.test.js +171 -0
- package/dist/__tests__/patch-layout.test.js.map +1 -0
- package/dist/__tests__/registry.test.js +795 -0
- package/dist/__tests__/registry.test.js.map +1 -0
- package/dist/__tests__/resources.test.js +139 -0
- package/dist/__tests__/resources.test.js.map +1 -0
- package/dist/__tests__/update-global.test.js +157 -0
- package/dist/__tests__/update-global.test.js.map +1 -0
- package/dist/api-keys.d.ts +46 -0
- package/dist/api-keys.js +272 -0
- package/dist/api-keys.js.map +1 -0
- package/dist/auth-strategy.d.ts +85 -0
- package/dist/auth-strategy.js +219 -0
- package/dist/auth-strategy.js.map +1 -0
- package/dist/components/CollectionScopesMatrix.d.ts +8 -0
- package/dist/components/CollectionScopesMatrix.js +32 -0
- package/dist/components/CollectionScopesMatrix.js.map +1 -0
- package/dist/components/GlobalScopesMatrix.d.ts +8 -0
- package/dist/components/GlobalScopesMatrix.js +28 -0
- package/dist/components/GlobalScopesMatrix.js.map +1 -0
- package/dist/components/ScopesTable.d.ts +19 -0
- package/dist/components/ScopesTable.js +285 -0
- package/dist/components/ScopesTable.js.map +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +4 -0
- package/dist/components/index.js.map +1 -0
- package/dist/conflict-detection.d.ts +13 -0
- package/dist/conflict-detection.js +41 -0
- package/dist/conflict-detection.js.map +1 -0
- package/dist/draft-workflow.d.ts +46 -47
- package/dist/draft-workflow.js +53 -130
- package/dist/draft-workflow.js.map +1 -1
- package/dist/endpoint.d.ts +35 -0
- package/dist/endpoint.js +105 -0
- package/dist/endpoint.js.map +1 -0
- package/dist/hash.d.ts +21 -0
- package/dist/hash.js +36 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +9 -9
- package/dist/index.js +168 -68
- package/dist/index.js.map +1 -1
- package/dist/introspection.d.ts +17 -3
- package/dist/introspection.js +95 -36
- package/dist/introspection.js.map +1 -1
- package/dist/prompts.js +5 -5
- package/dist/prompts.js.map +1 -1
- package/dist/registry.d.ts +50 -0
- package/dist/registry.js +169 -0
- package/dist/registry.js.map +1 -0
- package/dist/resources.d.ts +5 -3
- package/dist/resources.js +23 -11
- package/dist/resources.js.map +1 -1
- package/dist/scope/audit-log.d.ts +18 -0
- package/dist/scope/audit-log.js +50 -0
- package/dist/scope/audit-log.js.map +1 -0
- package/dist/scope/policy.d.ts +73 -0
- package/dist/scope/policy.js +218 -0
- package/dist/scope/policy.js.map +1 -0
- package/dist/tools/_helpers.d.ts +28 -1
- package/dist/tools/_helpers.js +83 -0
- package/dist/tools/_helpers.js.map +1 -1
- package/dist/tools/_layout-helpers.d.ts +43 -0
- package/dist/tools/_layout-helpers.js +159 -0
- package/dist/tools/_layout-helpers.js.map +1 -0
- package/dist/tools/create-document.d.ts +36 -0
- package/dist/tools/create-document.js +83 -0
- package/dist/tools/create-document.js.map +1 -0
- package/dist/tools/delete-document.d.ts +25 -0
- package/dist/tools/delete-document.js +49 -0
- package/dist/tools/delete-document.js.map +1 -0
- package/dist/tools/find-document.d.ts +33 -0
- package/dist/tools/find-document.js +97 -0
- package/dist/tools/find-document.js.map +1 -0
- package/dist/tools/find-global.d.ts +26 -0
- package/dist/tools/find-global.js +122 -0
- package/dist/tools/find-global.js.map +1 -0
- package/dist/tools/global-versions.d.ts +39 -0
- package/dist/tools/global-versions.js +132 -0
- package/dist/tools/global-versions.js.map +1 -0
- package/dist/tools/patch-global-layout.d.ts +31 -0
- package/dist/tools/patch-global-layout.js +127 -0
- package/dist/tools/patch-global-layout.js.map +1 -0
- package/dist/tools/patch-layout.d.ts +5 -8
- package/dist/tools/patch-layout.js +18 -100
- package/dist/tools/patch-layout.js.map +1 -1
- package/dist/tools/publish-draft.d.ts +5 -4
- package/dist/tools/publish-draft.js +6 -1
- package/dist/tools/publish-draft.js.map +1 -1
- package/dist/tools/publish-global-draft.d.ts +20 -0
- package/dist/tools/publish-global-draft.js +50 -0
- package/dist/tools/publish-global-draft.js.map +1 -0
- package/dist/tools/resolve-reference.d.ts +5 -4
- package/dist/tools/resolve-reference.js +4 -0
- package/dist/tools/resolve-reference.js.map +1 -1
- package/dist/tools/safe-delete.d.ts +5 -5
- package/dist/tools/safe-delete.js +20 -15
- package/dist/tools/safe-delete.js.map +1 -1
- package/dist/tools/schedule-publish.d.ts +5 -5
- package/dist/tools/schedule-publish.js +23 -19
- package/dist/tools/schedule-publish.js.map +1 -1
- package/dist/tools/search-content.d.ts +5 -9
- package/dist/tools/search-content.js +16 -12
- package/dist/tools/search-content.js.map +1 -1
- package/dist/tools/update-document.d.ts +5 -5
- package/dist/tools/update-document.js +10 -5
- package/dist/tools/update-document.js.map +1 -1
- package/dist/tools/update-global.d.ts +27 -0
- package/dist/tools/update-global.js +72 -0
- package/dist/tools/update-global.js.map +1 -0
- package/dist/tools/upload-media.d.ts +5 -4
- package/dist/tools/upload-media.js +6 -1
- package/dist/tools/upload-media.js.map +1 -1
- package/dist/tools/versions.d.ts +10 -9
- package/dist/tools/versions.js +15 -7
- package/dist/tools/versions.js.map +1 -1
- package/dist/types.d.ts +56 -3
- package/dist/types.js +13 -6
- package/dist/types.js.map +1 -1
- package/package.json +11 -4
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { errorMessage, jsonResponse, slugEnum, stampMcpContext, textResponse } from './_helpers';
|
|
3
|
+
/**
|
|
4
|
+
* Read any global by slug. Mirrors `findDocument`'s slug-enum + draft +
|
|
5
|
+
* preview behaviour but acts on Payload's `findGlobal` Local API method —
|
|
6
|
+
* globals are singletons, so there's no `id`, `where`, or `limit`.
|
|
7
|
+
*
|
|
8
|
+
* Preview stamping uses each global's `admin.livePreview.url` (preferred)
|
|
9
|
+
* or `admin.preview` function, same fallback chain as collections.
|
|
10
|
+
*/ export function createFindGlobalTool(globalSchemas, draftGlobals, globalsBySlug, previewSiteUrl, previewDisabled = false) {
|
|
11
|
+
const findableSlugs = [
|
|
12
|
+
...globalSchemas.keys()
|
|
13
|
+
];
|
|
14
|
+
const descriptionLines = findableSlugs.map((slug)=>` - "${slug}"${draftGlobals.has(slug) ? ' (draft-enabled)' : ''}`);
|
|
15
|
+
return {
|
|
16
|
+
name: 'findGlobal',
|
|
17
|
+
routing: {
|
|
18
|
+
kind: 'global',
|
|
19
|
+
action: 'read'
|
|
20
|
+
},
|
|
21
|
+
description: 'Read a global (a singleton site-wide settings document) by slug. ' + 'Globals carry things like site name, footer config, navigation menus — there is no `id`, no list, no `where` filter. ' + 'Use `draft: true` to read the draft version of a draft-enabled global.\n\n' + 'Available globals:\n' + descriptionLines.join('\n'),
|
|
22
|
+
parameters: {
|
|
23
|
+
slug: slugEnum(findableSlugs, 'global').describe(`The global slug. One of: ${findableSlugs.join(', ')}`),
|
|
24
|
+
draft: z.boolean().optional().describe('When true, returns the draft version of a draft-enabled global. Default false (published).'),
|
|
25
|
+
depth: z.number().int().min(0).max(3).optional().describe('Relationship population depth. Default 1.'),
|
|
26
|
+
locale: z.string().optional().describe('Optional locale code (e.g. "en", "fr") for localized fields. Defaults to the request locale.')
|
|
27
|
+
},
|
|
28
|
+
handler: async (args, req, _extra)=>{
|
|
29
|
+
const { slug, draft, depth, locale } = args;
|
|
30
|
+
if (!globalSchemas.has(slug)) {
|
|
31
|
+
return textResponse(`Error: Unknown global "${slug}". Available: ${findableSlugs.join(', ')}`);
|
|
32
|
+
}
|
|
33
|
+
stampMcpContext(req);
|
|
34
|
+
const globalConfig = globalsBySlug.get(slug);
|
|
35
|
+
try {
|
|
36
|
+
const doc = await req.payload.findGlobal({
|
|
37
|
+
slug: slug,
|
|
38
|
+
depth: depth ?? 1,
|
|
39
|
+
draft: draft ?? false,
|
|
40
|
+
...locale ? {
|
|
41
|
+
locale: locale
|
|
42
|
+
} : {},
|
|
43
|
+
req,
|
|
44
|
+
overrideAccess: false,
|
|
45
|
+
user: req.user
|
|
46
|
+
});
|
|
47
|
+
const base = jsonResponse(doc);
|
|
48
|
+
if (previewDisabled || !globalConfig) return base;
|
|
49
|
+
const isDraft = doc?._status === 'draft';
|
|
50
|
+
if (!isDraft) return base;
|
|
51
|
+
const previewUrl = await resolveGlobalPreviewUrl(globalConfig, doc, req, previewSiteUrl);
|
|
52
|
+
const hint = previewUrl ? `\n📋 This global is in draft status. Preview it here: ${previewUrl}` : '\n📋 This global is in draft status. Use the admin panel to preview it.';
|
|
53
|
+
return {
|
|
54
|
+
content: [
|
|
55
|
+
...base.content,
|
|
56
|
+
{
|
|
57
|
+
type: 'text',
|
|
58
|
+
text: hint
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
};
|
|
62
|
+
} catch (err) {
|
|
63
|
+
return textResponse(`Error reading global "${slug}": ${errorMessage(err)}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Resolve a preview URL from a global's `admin.livePreview.url` / `admin.preview`
|
|
70
|
+
* config. Same fallback chain as `resolvePreviewUrl` for collections, but
|
|
71
|
+
* passes `globalConfig` (not `collectionConfig`) to the user callback.
|
|
72
|
+
*/ async function resolveGlobalPreviewUrl(global, doc, req, siteUrl) {
|
|
73
|
+
const admin = global.admin ?? {};
|
|
74
|
+
const locale = req.locale ?? 'en';
|
|
75
|
+
let raw;
|
|
76
|
+
const livePreviewUrl = admin.livePreview?.url;
|
|
77
|
+
if (typeof livePreviewUrl === 'function') {
|
|
78
|
+
try {
|
|
79
|
+
raw = await livePreviewUrl({
|
|
80
|
+
data: doc,
|
|
81
|
+
locale: {
|
|
82
|
+
code: locale,
|
|
83
|
+
label: locale
|
|
84
|
+
},
|
|
85
|
+
req,
|
|
86
|
+
payload: req.payload,
|
|
87
|
+
globalConfig: global
|
|
88
|
+
});
|
|
89
|
+
} catch (err) {
|
|
90
|
+
req.payload.logger?.warn?.({
|
|
91
|
+
err,
|
|
92
|
+
slug: global.slug
|
|
93
|
+
}, `[payload-mcp-toolkit] admin.livePreview.url threw for global "${global.slug}"`);
|
|
94
|
+
raw = null;
|
|
95
|
+
}
|
|
96
|
+
} else if (typeof livePreviewUrl === 'string') {
|
|
97
|
+
raw = livePreviewUrl;
|
|
98
|
+
}
|
|
99
|
+
if (!raw && typeof admin.preview === 'function') {
|
|
100
|
+
try {
|
|
101
|
+
raw = await admin.preview(doc, {
|
|
102
|
+
locale,
|
|
103
|
+
req,
|
|
104
|
+
token: null
|
|
105
|
+
});
|
|
106
|
+
} catch (err) {
|
|
107
|
+
req.payload.logger?.warn?.({
|
|
108
|
+
err,
|
|
109
|
+
slug: global.slug
|
|
110
|
+
}, `[payload-mcp-toolkit] admin.preview threw for global "${global.slug}"`);
|
|
111
|
+
raw = null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (!raw || typeof raw !== 'string') return null;
|
|
115
|
+
if (raw.startsWith('http://') || raw.startsWith('https://')) return raw;
|
|
116
|
+
if (!siteUrl) return null;
|
|
117
|
+
const base = siteUrl.endsWith('/') ? siteUrl.slice(0, -1) : siteUrl;
|
|
118
|
+
const path = raw.startsWith('/') ? raw : `/${raw}`;
|
|
119
|
+
return `${base}${path}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
//# sourceMappingURL=find-global.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/tools/find-global.ts"],"sourcesContent":["import { z } from 'zod'\r\nimport type { GlobalConfig, PayloadRequest } from 'payload'\r\nimport type { GlobalSchema } from '../types'\r\nimport { errorMessage, jsonResponse, slugEnum, stampMcpContext, textResponse } from './_helpers'\r\n\r\ninterface FindGlobalArgs {\r\n slug: string\r\n draft?: boolean\r\n depth?: number\r\n locale?: string\r\n}\r\n\r\n/**\r\n * Read any global by slug. Mirrors `findDocument`'s slug-enum + draft +\r\n * preview behaviour but acts on Payload's `findGlobal` Local API method —\r\n * globals are singletons, so there's no `id`, `where`, or `limit`.\r\n *\r\n * Preview stamping uses each global's `admin.livePreview.url` (preferred)\r\n * or `admin.preview` function, same fallback chain as collections.\r\n */\r\nexport function createFindGlobalTool(\r\n globalSchemas: Map<string, GlobalSchema>,\r\n draftGlobals: Set<string>,\r\n globalsBySlug: Map<string, GlobalConfig>,\r\n previewSiteUrl: string | undefined,\r\n previewDisabled = false,\r\n) {\r\n const findableSlugs = [...globalSchemas.keys()]\r\n const descriptionLines = findableSlugs.map(\r\n (slug) => ` - \"${slug}\"${draftGlobals.has(slug) ? ' (draft-enabled)' : ''}`,\r\n )\r\n\r\n return {\r\n name: 'findGlobal',\r\n routing: { kind: 'global', action: 'read' } as const,\r\n description:\r\n 'Read a global (a singleton site-wide settings document) by slug. ' +\r\n 'Globals carry things like site name, footer config, navigation menus — there is no `id`, no list, no `where` filter. ' +\r\n 'Use `draft: true` to read the draft version of a draft-enabled global.\\n\\n' +\r\n 'Available globals:\\n' +\r\n descriptionLines.join('\\n'),\r\n parameters: {\r\n slug: slugEnum(findableSlugs, 'global').describe(\r\n `The global slug. One of: ${findableSlugs.join(', ')}`,\r\n ),\r\n draft: z\r\n .boolean()\r\n .optional()\r\n .describe(\r\n 'When true, returns the draft version of a draft-enabled global. Default false (published).',\r\n ),\r\n depth: z\r\n .number()\r\n .int()\r\n .min(0)\r\n .max(3)\r\n .optional()\r\n .describe('Relationship population depth. Default 1.'),\r\n locale: z\r\n .string()\r\n .optional()\r\n .describe(\r\n 'Optional locale code (e.g. \"en\", \"fr\") for localized fields. Defaults to the request locale.',\r\n ),\r\n },\r\n handler: async (args: Record<string, unknown>, req: PayloadRequest, _extra: unknown) => {\r\n const { slug, draft, depth, locale } = args as unknown as FindGlobalArgs\r\n\r\n if (!globalSchemas.has(slug)) {\r\n return textResponse(\r\n `Error: Unknown global \"${slug}\". Available: ${findableSlugs.join(', ')}`,\r\n )\r\n }\r\n\r\n stampMcpContext(req)\r\n const globalConfig = globalsBySlug.get(slug)\r\n\r\n try {\r\n const doc = await req.payload.findGlobal({\r\n slug: slug as never,\r\n depth: depth ?? 1,\r\n draft: draft ?? false,\r\n ...(locale ? { locale: locale as never } : {}),\r\n req,\r\n overrideAccess: false,\r\n user: req.user,\r\n })\r\n\r\n const base = jsonResponse(doc)\r\n if (previewDisabled || !globalConfig) return base\r\n\r\n const isDraft =\r\n (doc as Record<string, unknown> | undefined)?._status === 'draft'\r\n if (!isDraft) return base\r\n\r\n const previewUrl = await resolveGlobalPreviewUrl(\r\n globalConfig,\r\n doc as Record<string, unknown>,\r\n req,\r\n previewSiteUrl,\r\n )\r\n const hint = previewUrl\r\n ? `\\n📋 This global is in draft status. Preview it here: ${previewUrl}`\r\n : '\\n📋 This global is in draft status. Use the admin panel to preview it.'\r\n return { content: [...base.content, { type: 'text' as const, text: hint }] }\r\n } catch (err) {\r\n return textResponse(`Error reading global \"${slug}\": ${errorMessage(err)}`)\r\n }\r\n },\r\n }\r\n}\r\n\r\n/**\r\n * Resolve a preview URL from a global's `admin.livePreview.url` / `admin.preview`\r\n * config. Same fallback chain as `resolvePreviewUrl` for collections, but\r\n * passes `globalConfig` (not `collectionConfig`) to the user callback.\r\n */\r\nasync function resolveGlobalPreviewUrl(\r\n global: GlobalConfig,\r\n doc: Record<string, unknown>,\r\n req: PayloadRequest,\r\n siteUrl: string | undefined,\r\n): Promise<string | null> {\r\n const admin = (global.admin ?? {}) as Record<string, any>\r\n const locale = (req as unknown as { locale?: string }).locale ?? 'en'\r\n\r\n let raw: string | null | undefined\r\n\r\n const livePreviewUrl = admin.livePreview?.url\r\n if (typeof livePreviewUrl === 'function') {\r\n try {\r\n raw = await livePreviewUrl({\r\n data: doc,\r\n locale: { code: locale, label: locale },\r\n req,\r\n payload: req.payload,\r\n globalConfig: global,\r\n })\r\n } catch (err) {\r\n req.payload.logger?.warn?.(\r\n { err, slug: global.slug },\r\n `[payload-mcp-toolkit] admin.livePreview.url threw for global \"${global.slug}\"`,\r\n )\r\n raw = null\r\n }\r\n } else if (typeof livePreviewUrl === 'string') {\r\n raw = livePreviewUrl\r\n }\r\n\r\n if (!raw && typeof admin.preview === 'function') {\r\n try {\r\n raw = await admin.preview(doc, { locale, req, token: null })\r\n } catch (err) {\r\n req.payload.logger?.warn?.(\r\n { err, slug: global.slug },\r\n `[payload-mcp-toolkit] admin.preview threw for global \"${global.slug}\"`,\r\n )\r\n raw = null\r\n }\r\n }\r\n\r\n if (!raw || typeof raw !== 'string') return null\r\n if (raw.startsWith('http://') || raw.startsWith('https://')) return raw\r\n if (!siteUrl) return null\r\n\r\n const base = siteUrl.endsWith('/') ? siteUrl.slice(0, -1) : siteUrl\r\n const path = raw.startsWith('/') ? raw : `/${raw}`\r\n return `${base}${path}`\r\n}\r\n"],"names":["z","errorMessage","jsonResponse","slugEnum","stampMcpContext","textResponse","createFindGlobalTool","globalSchemas","draftGlobals","globalsBySlug","previewSiteUrl","previewDisabled","findableSlugs","keys","descriptionLines","map","slug","has","name","routing","kind","action","description","join","parameters","describe","draft","boolean","optional","depth","number","int","min","max","locale","string","handler","args","req","_extra","globalConfig","get","doc","payload","findGlobal","overrideAccess","user","base","isDraft","_status","previewUrl","resolveGlobalPreviewUrl","hint","content","type","text","err","global","siteUrl","admin","raw","livePreviewUrl","livePreview","url","data","code","label","logger","warn","preview","token","startsWith","endsWith","slice","path"],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PayloadRequest } from 'payload';
|
|
3
|
+
/**
|
|
4
|
+
* List recent saved versions of a draft-enabled global. Returns null when no
|
|
5
|
+
* global has drafts enabled so the plugin entry can skip registration.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createListGlobalVersionsTool(draftGlobals: Set<string>): {
|
|
8
|
+
name: string;
|
|
9
|
+
routing: {
|
|
10
|
+
readonly kind: "global";
|
|
11
|
+
readonly action: "read";
|
|
12
|
+
};
|
|
13
|
+
description: string;
|
|
14
|
+
parameters: {
|
|
15
|
+
slug: z.ZodEnum<[string, ...string[]]>;
|
|
16
|
+
limit: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
17
|
+
locale: z.ZodOptional<z.ZodString>;
|
|
18
|
+
};
|
|
19
|
+
handler: (args: Record<string, unknown>, req: PayloadRequest, _extra: unknown) => Promise<import("./_helpers").McpTextResponse>;
|
|
20
|
+
} | null;
|
|
21
|
+
/**
|
|
22
|
+
* Restore a draft-enabled global to a previously saved version. Returns null
|
|
23
|
+
* when no global has drafts enabled.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createRestoreGlobalVersionTool(draftGlobals: Set<string>): {
|
|
26
|
+
name: string;
|
|
27
|
+
routing: {
|
|
28
|
+
readonly kind: "global";
|
|
29
|
+
readonly action: "update";
|
|
30
|
+
};
|
|
31
|
+
description: string;
|
|
32
|
+
parameters: {
|
|
33
|
+
slug: z.ZodEnum<[string, ...string[]]>;
|
|
34
|
+
versionId: z.ZodString;
|
|
35
|
+
expectedUpdatedAt: z.ZodOptional<z.ZodString>;
|
|
36
|
+
locale: z.ZodOptional<z.ZodString>;
|
|
37
|
+
};
|
|
38
|
+
handler: (args: Record<string, unknown>, req: PayloadRequest, _extra: unknown) => Promise<import("./_helpers").McpTextResponse>;
|
|
39
|
+
} | null;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { errorMessage, jsonResponse, slugEnum, stampMcpContext, textResponse } from './_helpers';
|
|
3
|
+
const DEFAULT_LIST_LIMIT = 10;
|
|
4
|
+
/**
|
|
5
|
+
* List recent saved versions of a draft-enabled global. Returns null when no
|
|
6
|
+
* global has drafts enabled so the plugin entry can skip registration.
|
|
7
|
+
*/ export function createListGlobalVersionsTool(draftGlobals) {
|
|
8
|
+
if (draftGlobals.size === 0) return null;
|
|
9
|
+
const slugs = [
|
|
10
|
+
...draftGlobals
|
|
11
|
+
];
|
|
12
|
+
return {
|
|
13
|
+
name: 'listGlobalVersions',
|
|
14
|
+
routing: {
|
|
15
|
+
kind: 'global',
|
|
16
|
+
action: 'read'
|
|
17
|
+
},
|
|
18
|
+
description: 'List recent saved versions of a draft-enabled global. ' + 'Use before restoreGlobalVersion to pick the right point in time. ' + `Draft-enabled globals: ${slugs.join(', ')}`,
|
|
19
|
+
parameters: {
|
|
20
|
+
slug: slugEnum(slugs, 'global').describe(`The global slug. One of: ${slugs.join(', ')}`),
|
|
21
|
+
limit: z.number().optional().default(DEFAULT_LIST_LIMIT).describe(`Maximum number of versions to return (default ${DEFAULT_LIST_LIMIT})`),
|
|
22
|
+
locale: z.string().optional().describe('Optional locale code (e.g. "en", "fr") to filter version snapshots to.')
|
|
23
|
+
},
|
|
24
|
+
handler: async (args, req, _extra)=>{
|
|
25
|
+
const { slug, limit = DEFAULT_LIST_LIMIT, locale } = args;
|
|
26
|
+
if (!draftGlobals.has(slug)) {
|
|
27
|
+
return textResponse(`Error: Global "${slug}" does not support versions. Draft-enabled globals: ${slugs.join(', ') || 'none'}`);
|
|
28
|
+
}
|
|
29
|
+
stampMcpContext(req);
|
|
30
|
+
try {
|
|
31
|
+
const result = await req.payload.findGlobalVersions({
|
|
32
|
+
slug: slug,
|
|
33
|
+
sort: '-updatedAt',
|
|
34
|
+
limit,
|
|
35
|
+
...locale ? {
|
|
36
|
+
locale: locale
|
|
37
|
+
} : {},
|
|
38
|
+
req,
|
|
39
|
+
overrideAccess: false,
|
|
40
|
+
user: req.user
|
|
41
|
+
});
|
|
42
|
+
const versions = result.docs.map((v)=>{
|
|
43
|
+
const snapshot = v.version || {};
|
|
44
|
+
return {
|
|
45
|
+
id: v.id,
|
|
46
|
+
updatedAt: v.updatedAt,
|
|
47
|
+
createdAt: v.createdAt,
|
|
48
|
+
status: snapshot._status ?? 'unknown',
|
|
49
|
+
autosave: v.autosave === true
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
return jsonResponse({
|
|
53
|
+
slug,
|
|
54
|
+
totalDocs: result.totalDocs,
|
|
55
|
+
returned: versions.length,
|
|
56
|
+
versions
|
|
57
|
+
});
|
|
58
|
+
} catch (err) {
|
|
59
|
+
return textResponse(`Error listing versions for global "${slug}": ${errorMessage(err)}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Restore a draft-enabled global to a previously saved version. Returns null
|
|
66
|
+
* when no global has drafts enabled.
|
|
67
|
+
*/ export function createRestoreGlobalVersionTool(draftGlobals) {
|
|
68
|
+
if (draftGlobals.size === 0) return null;
|
|
69
|
+
const slugs = [
|
|
70
|
+
...draftGlobals
|
|
71
|
+
];
|
|
72
|
+
return {
|
|
73
|
+
name: 'restoreGlobalVersion',
|
|
74
|
+
routing: {
|
|
75
|
+
kind: 'global',
|
|
76
|
+
action: 'update'
|
|
77
|
+
},
|
|
78
|
+
description: 'Restore a global to a previously saved version. ' + 'Use listGlobalVersions first to find the version ID. ' + 'Restoring creates a new version on top, so the previous state is recoverable. ' + `Draft-enabled globals: ${slugs.join(', ')}`,
|
|
79
|
+
parameters: {
|
|
80
|
+
slug: slugEnum(slugs, 'global').describe(`The global slug. One of: ${slugs.join(', ')}`),
|
|
81
|
+
versionId: z.string().describe('The version ID returned by listGlobalVersions (NOT the global slug)'),
|
|
82
|
+
expectedUpdatedAt: z.string().optional().describe('Optional CAS guard. If set, the restore is rejected when the global\'s current updatedAt differs from this value — protects against clobbering concurrent edits. Pass the updatedAt returned by a prior findGlobal call.'),
|
|
83
|
+
locale: z.string().optional().describe('Optional locale code (e.g. "en", "fr") to scope the restore to a single locale.')
|
|
84
|
+
},
|
|
85
|
+
handler: async (args, req, _extra)=>{
|
|
86
|
+
const { slug, versionId, expectedUpdatedAt, locale } = args;
|
|
87
|
+
if (!draftGlobals.has(slug)) {
|
|
88
|
+
return textResponse(`Error: Global "${slug}" does not support versions. Draft-enabled globals: ${slugs.join(', ') || 'none'}`);
|
|
89
|
+
}
|
|
90
|
+
stampMcpContext(req);
|
|
91
|
+
if (expectedUpdatedAt !== undefined) {
|
|
92
|
+
try {
|
|
93
|
+
const current = await req.payload.findGlobal({
|
|
94
|
+
slug: slug,
|
|
95
|
+
depth: 0,
|
|
96
|
+
draft: true,
|
|
97
|
+
...locale ? {
|
|
98
|
+
locale: locale
|
|
99
|
+
} : {},
|
|
100
|
+
req,
|
|
101
|
+
overrideAccess: false,
|
|
102
|
+
user: req.user
|
|
103
|
+
});
|
|
104
|
+
const currentUpdatedAt = current?.updatedAt;
|
|
105
|
+
const currentStr = typeof currentUpdatedAt === 'string' ? currentUpdatedAt : currentUpdatedAt instanceof Date ? currentUpdatedAt.toISOString() : undefined;
|
|
106
|
+
if (currentStr !== expectedUpdatedAt) {
|
|
107
|
+
return textResponse(`Conflict: global "${slug}" was modified since expectedUpdatedAt (${expectedUpdatedAt}); current updatedAt is ${currentStr ?? 'unknown'}. Re-read the global and retry.`);
|
|
108
|
+
}
|
|
109
|
+
} catch (err) {
|
|
110
|
+
return textResponse(`Error checking expectedUpdatedAt on global "${slug}": ${errorMessage(err)}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
await req.payload.restoreGlobalVersion({
|
|
115
|
+
slug: slug,
|
|
116
|
+
id: versionId,
|
|
117
|
+
...locale ? {
|
|
118
|
+
locale: locale
|
|
119
|
+
} : {},
|
|
120
|
+
req,
|
|
121
|
+
overrideAccess: false,
|
|
122
|
+
user: req.user
|
|
123
|
+
});
|
|
124
|
+
return textResponse(`Restored global "${slug}" from version ${versionId}. ` + `The global is now in draft status — use publishGlobalDraft to make the restored content live.`);
|
|
125
|
+
} catch (err) {
|
|
126
|
+
return textResponse(`Error restoring global "${slug}" from version ${versionId}: ${errorMessage(err)}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
//# sourceMappingURL=global-versions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/tools/global-versions.ts"],"sourcesContent":["import { z } from 'zod'\r\nimport type { PayloadRequest } from 'payload'\r\nimport { errorMessage, jsonResponse, slugEnum, stampMcpContext, textResponse } from './_helpers'\r\n\r\nconst DEFAULT_LIST_LIMIT = 10\r\n\r\n/**\r\n * List recent saved versions of a draft-enabled global. Returns null when no\r\n * global has drafts enabled so the plugin entry can skip registration.\r\n */\r\nexport function createListGlobalVersionsTool(draftGlobals: Set<string>) {\r\n if (draftGlobals.size === 0) return null\r\n\r\n const slugs = [...draftGlobals]\r\n return {\r\n name: 'listGlobalVersions',\r\n routing: { kind: 'global', action: 'read' } as const,\r\n description:\r\n 'List recent saved versions of a draft-enabled global. ' +\r\n 'Use before restoreGlobalVersion to pick the right point in time. ' +\r\n `Draft-enabled globals: ${slugs.join(', ')}`,\r\n parameters: {\r\n slug: slugEnum(slugs, 'global').describe(`The global slug. One of: ${slugs.join(', ')}`),\r\n limit: z\r\n .number()\r\n .optional()\r\n .default(DEFAULT_LIST_LIMIT)\r\n .describe(`Maximum number of versions to return (default ${DEFAULT_LIST_LIMIT})`),\r\n locale: z\r\n .string()\r\n .optional()\r\n .describe('Optional locale code (e.g. \"en\", \"fr\") to filter version snapshots to.'),\r\n },\r\n handler: async (\r\n args: Record<string, unknown>,\r\n req: PayloadRequest,\r\n _extra: unknown,\r\n ) => {\r\n const { slug, limit = DEFAULT_LIST_LIMIT, locale } = args as {\r\n slug: string\r\n limit?: number\r\n locale?: string\r\n }\r\n\r\n if (!draftGlobals.has(slug)) {\r\n return textResponse(\r\n `Error: Global \"${slug}\" does not support versions. Draft-enabled globals: ${slugs.join(', ') || 'none'}`,\r\n )\r\n }\r\n\r\n stampMcpContext(req)\r\n\r\n try {\r\n const result = await req.payload.findGlobalVersions({\r\n slug: slug as never,\r\n sort: '-updatedAt',\r\n limit,\r\n ...(locale ? { locale: locale as never } : {}),\r\n req,\r\n overrideAccess: false,\r\n user: req.user,\r\n })\r\n\r\n const versions = result.docs.map((v: any) => {\r\n const snapshot = v.version || {}\r\n return {\r\n id: v.id,\r\n updatedAt: v.updatedAt,\r\n createdAt: v.createdAt,\r\n status: snapshot._status ?? 'unknown',\r\n autosave: v.autosave === true,\r\n }\r\n })\r\n\r\n return jsonResponse({\r\n slug,\r\n totalDocs: result.totalDocs,\r\n returned: versions.length,\r\n versions,\r\n })\r\n } catch (err) {\r\n return textResponse(`Error listing versions for global \"${slug}\": ${errorMessage(err)}`)\r\n }\r\n },\r\n }\r\n}\r\n\r\n/**\r\n * Restore a draft-enabled global to a previously saved version. Returns null\r\n * when no global has drafts enabled.\r\n */\r\nexport function createRestoreGlobalVersionTool(draftGlobals: Set<string>) {\r\n if (draftGlobals.size === 0) return null\r\n\r\n const slugs = [...draftGlobals]\r\n return {\r\n name: 'restoreGlobalVersion',\r\n routing: { kind: 'global', action: 'update' } as const,\r\n description:\r\n 'Restore a global to a previously saved version. ' +\r\n 'Use listGlobalVersions first to find the version ID. ' +\r\n 'Restoring creates a new version on top, so the previous state is recoverable. ' +\r\n `Draft-enabled globals: ${slugs.join(', ')}`,\r\n parameters: {\r\n slug: slugEnum(slugs, 'global').describe(`The global slug. One of: ${slugs.join(', ')}`),\r\n versionId: z\r\n .string()\r\n .describe('The version ID returned by listGlobalVersions (NOT the global slug)'),\r\n expectedUpdatedAt: z\r\n .string()\r\n .optional()\r\n .describe(\r\n 'Optional CAS guard. If set, the restore is rejected when the global\\'s current updatedAt differs from this value — protects against clobbering concurrent edits. Pass the updatedAt returned by a prior findGlobal call.',\r\n ),\r\n locale: z\r\n .string()\r\n .optional()\r\n .describe('Optional locale code (e.g. \"en\", \"fr\") to scope the restore to a single locale.'),\r\n },\r\n handler: async (\r\n args: Record<string, unknown>,\r\n req: PayloadRequest,\r\n _extra: unknown,\r\n ) => {\r\n const { slug, versionId, expectedUpdatedAt, locale } = args as {\r\n slug: string\r\n versionId: string\r\n expectedUpdatedAt?: string\r\n locale?: string\r\n }\r\n\r\n if (!draftGlobals.has(slug)) {\r\n return textResponse(\r\n `Error: Global \"${slug}\" does not support versions. Draft-enabled globals: ${slugs.join(', ') || 'none'}`,\r\n )\r\n }\r\n\r\n stampMcpContext(req)\r\n\r\n if (expectedUpdatedAt !== undefined) {\r\n try {\r\n const current = (await req.payload.findGlobal({\r\n slug: slug as never,\r\n depth: 0,\r\n draft: true,\r\n ...(locale ? { locale: locale as never } : {}),\r\n req,\r\n overrideAccess: false,\r\n user: req.user,\r\n })) as { updatedAt?: unknown } | undefined\r\n const currentUpdatedAt = current?.updatedAt\r\n const currentStr =\r\n typeof currentUpdatedAt === 'string'\r\n ? currentUpdatedAt\r\n : currentUpdatedAt instanceof Date\r\n ? currentUpdatedAt.toISOString()\r\n : undefined\r\n if (currentStr !== expectedUpdatedAt) {\r\n return textResponse(\r\n `Conflict: global \"${slug}\" was modified since expectedUpdatedAt (${expectedUpdatedAt}); current updatedAt is ${currentStr ?? 'unknown'}. Re-read the global and retry.`,\r\n )\r\n }\r\n } catch (err) {\r\n return textResponse(\r\n `Error checking expectedUpdatedAt on global \"${slug}\": ${errorMessage(err)}`,\r\n )\r\n }\r\n }\r\n\r\n try {\r\n await req.payload.restoreGlobalVersion({\r\n slug: slug as never,\r\n id: versionId,\r\n ...(locale ? { locale: locale as never } : {}),\r\n req,\r\n overrideAccess: false,\r\n user: req.user,\r\n })\r\n return textResponse(\r\n `Restored global \"${slug}\" from version ${versionId}. ` +\r\n `The global is now in draft status — use publishGlobalDraft to make the restored content live.`,\r\n )\r\n } catch (err) {\r\n return textResponse(\r\n `Error restoring global \"${slug}\" from version ${versionId}: ${errorMessage(err)}`,\r\n )\r\n }\r\n },\r\n }\r\n}\r\n"],"names":["z","errorMessage","jsonResponse","slugEnum","stampMcpContext","textResponse","DEFAULT_LIST_LIMIT","createListGlobalVersionsTool","draftGlobals","size","slugs","name","routing","kind","action","description","join","parameters","slug","describe","limit","number","optional","default","locale","string","handler","args","req","_extra","has","result","payload","findGlobalVersions","sort","overrideAccess","user","versions","docs","map","v","snapshot","version","id","updatedAt","createdAt","status","_status","autosave","totalDocs","returned","length","err","createRestoreGlobalVersionTool","versionId","expectedUpdatedAt","undefined","current","findGlobal","depth","draft","currentUpdatedAt","currentStr","Date","toISOString","restoreGlobalVersion"],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PayloadRequest } from 'payload';
|
|
3
|
+
import type { BlockCatalog, BlockNestingMap } from '../types';
|
|
4
|
+
/**
|
|
5
|
+
* patchGlobalLayout — surgical wrapper that mutates a single blocks-typed
|
|
6
|
+
* field on a global without round-tripping the entire array through
|
|
7
|
+
* updateGlobal. Same operation grammar as patchLayout (append, prepend,
|
|
8
|
+
* insertAt, replaceAt, full); same recursive nesting validator against the
|
|
9
|
+
* unified BlockNestingMap.
|
|
10
|
+
*
|
|
11
|
+
* Returns null when no global has any blocks field — the plugin entry
|
|
12
|
+
* skips registration in that case, mirroring the schedulePublish pattern.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createPatchGlobalLayoutTool(catalog: BlockCatalog, nesting: BlockNestingMap, draftGlobals: Set<string>): {
|
|
15
|
+
name: string;
|
|
16
|
+
routing: {
|
|
17
|
+
readonly kind: "global";
|
|
18
|
+
readonly action: "update";
|
|
19
|
+
};
|
|
20
|
+
description: string;
|
|
21
|
+
parameters: {
|
|
22
|
+
slug: z.ZodEnum<[string, ...string[]]>;
|
|
23
|
+
layoutField: z.ZodString;
|
|
24
|
+
blocks: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">;
|
|
25
|
+
operation: z.ZodEnum<["append", "prepend", "insertAt", "replaceAt", "full"]>;
|
|
26
|
+
insertIndex: z.ZodOptional<z.ZodNumber>;
|
|
27
|
+
expectedUpdatedAt: z.ZodOptional<z.ZodString>;
|
|
28
|
+
locale: z.ZodOptional<z.ZodString>;
|
|
29
|
+
};
|
|
30
|
+
handler: (args: Record<string, unknown>, req: PayloadRequest, _extra: unknown) => Promise<import("./_helpers").McpTextResponse>;
|
|
31
|
+
} | null;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { DRAFT_NOTE, errorMessage, jsonResponse, slugEnum, stampMcpContext } from './_helpers';
|
|
3
|
+
import { applyOperation, errorResponse, readPath, validateBlockList, writePath } from './_layout-helpers';
|
|
4
|
+
/**
|
|
5
|
+
* patchGlobalLayout — surgical wrapper that mutates a single blocks-typed
|
|
6
|
+
* field on a global without round-tripping the entire array through
|
|
7
|
+
* updateGlobal. Same operation grammar as patchLayout (append, prepend,
|
|
8
|
+
* insertAt, replaceAt, full); same recursive nesting validator against the
|
|
9
|
+
* unified BlockNestingMap.
|
|
10
|
+
*
|
|
11
|
+
* Returns null when no global has any blocks field — the plugin entry
|
|
12
|
+
* skips registration in that case, mirroring the schedulePublish pattern.
|
|
13
|
+
*/ export function createPatchGlobalLayoutTool(catalog, nesting, draftGlobals) {
|
|
14
|
+
const allBlockSlugs = new Set(catalog.blocks.map((b)=>b.slug));
|
|
15
|
+
const nestingByGlobalField = new Map();
|
|
16
|
+
const nestingByBlockField = new Map();
|
|
17
|
+
for (const edge of nesting){
|
|
18
|
+
const key = `${edge.owner}:${edge.fieldPath}`;
|
|
19
|
+
if (edge.ownerType === 'global') {
|
|
20
|
+
nestingByGlobalField.set(key, edge.acceptedBlockSlugs);
|
|
21
|
+
} else if (edge.ownerType === 'block') {
|
|
22
|
+
nestingByBlockField.set(key, edge.acceptedBlockSlugs);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Conditional registration: no global has a blocks field → no tool.
|
|
26
|
+
if (nestingByGlobalField.size === 0) return null;
|
|
27
|
+
const patchableSlugs = [
|
|
28
|
+
...new Set([
|
|
29
|
+
...nestingByGlobalField.keys()
|
|
30
|
+
].map((k)=>k.split(':')[0]))
|
|
31
|
+
];
|
|
32
|
+
return {
|
|
33
|
+
name: 'patchGlobalLayout',
|
|
34
|
+
routing: {
|
|
35
|
+
kind: 'global',
|
|
36
|
+
action: 'update'
|
|
37
|
+
},
|
|
38
|
+
description: 'Surgically modify a blocks-typed field on a global (e.g. footer sections, header nav) without sending the whole array. Same operation grammar as patchLayout. Use the blockNesting resource to see which slugs each field accepts; global-owned edges have ownerType "global".',
|
|
39
|
+
parameters: {
|
|
40
|
+
slug: slugEnum(patchableSlugs, 'global').describe(`The global slug whose blocks-typed field will be patched. One of: ${patchableSlugs.join(', ')}`),
|
|
41
|
+
layoutField: z.string().describe('Name (or dotted path) of the blocks-typed field on the global to patch — e.g. "layout", "footer.sections".'),
|
|
42
|
+
blocks: z.array(z.record(z.string(), z.unknown())).describe('Blocks to compose. Each must have a `blockType` discriminator plus any block-specific fields. Nested blocks fields hold their own `blocks` arrays at any depth.'),
|
|
43
|
+
operation: z.enum([
|
|
44
|
+
'append',
|
|
45
|
+
'prepend',
|
|
46
|
+
'insertAt',
|
|
47
|
+
'replaceAt',
|
|
48
|
+
'full'
|
|
49
|
+
]).describe('How to apply the blocks: append (end), prepend (start), insertAt (at index), replaceAt (overwrite N starting at index), full (replace entire array — use with care).'),
|
|
50
|
+
insertIndex: z.number().optional().describe('Index for insertAt/replaceAt operations'),
|
|
51
|
+
expectedUpdatedAt: z.string().optional().describe('Optional CAS guard. If set, the patch is rejected when the global\'s current updatedAt differs from this value — protects against lost writes when concurrent edits race. Pass the updatedAt returned by a prior findGlobal call.'),
|
|
52
|
+
locale: z.string().optional().describe('Optional locale code (e.g. "en", "fr") to scope the patch to a single locale on localized blocks fields.')
|
|
53
|
+
},
|
|
54
|
+
handler: async (args, req, _extra)=>{
|
|
55
|
+
const { slug, layoutField, blocks, operation, insertIndex, expectedUpdatedAt, locale } = args;
|
|
56
|
+
const rootKey = `${slug}:${layoutField}`;
|
|
57
|
+
const rootAllowed = nestingByGlobalField.get(rootKey);
|
|
58
|
+
if (!rootAllowed) {
|
|
59
|
+
return errorResponse(`Field "${layoutField}" on global "${slug}" is not a blocks-typed field, or no nesting map entry exists for it.`, {
|
|
60
|
+
availableFields: [
|
|
61
|
+
...nestingByGlobalField.keys()
|
|
62
|
+
]
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
const errors = [];
|
|
66
|
+
validateBlockList(blocks, rootAllowed, layoutField, allBlockSlugs, nestingByBlockField, errors);
|
|
67
|
+
if (errors.length > 0) {
|
|
68
|
+
return errorResponse('Block validation failed.', {
|
|
69
|
+
errors
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
stampMcpContext(req);
|
|
73
|
+
let existing;
|
|
74
|
+
try {
|
|
75
|
+
existing = await req.payload.findGlobal({
|
|
76
|
+
slug: slug,
|
|
77
|
+
depth: 0,
|
|
78
|
+
draft: true,
|
|
79
|
+
...locale ? {
|
|
80
|
+
locale: locale
|
|
81
|
+
} : {},
|
|
82
|
+
req,
|
|
83
|
+
overrideAccess: false,
|
|
84
|
+
user: req.user
|
|
85
|
+
});
|
|
86
|
+
} catch (error) {
|
|
87
|
+
return errorResponse(`Error fetching global "${slug}": ${errorMessage(error)}`);
|
|
88
|
+
}
|
|
89
|
+
if (expectedUpdatedAt !== undefined) {
|
|
90
|
+
const currentUpdatedAt = existing?.updatedAt;
|
|
91
|
+
const currentStr = typeof currentUpdatedAt === 'string' ? currentUpdatedAt : currentUpdatedAt instanceof Date ? currentUpdatedAt.toISOString() : undefined;
|
|
92
|
+
if (currentStr !== expectedUpdatedAt) {
|
|
93
|
+
return errorResponse(`Conflict: global "${slug}" was modified since expectedUpdatedAt (${expectedUpdatedAt}); current updatedAt is ${currentStr ?? 'unknown'}. Re-read the global and retry.`, {
|
|
94
|
+
currentUpdatedAt: currentStr
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const currentLayout = readPath(existing, layoutField);
|
|
99
|
+
const finalLayout = applyOperation(blocks, operation, insertIndex, currentLayout);
|
|
100
|
+
const isDraftGlobal = draftGlobals.has(slug);
|
|
101
|
+
try {
|
|
102
|
+
await req.payload.updateGlobal({
|
|
103
|
+
slug: slug,
|
|
104
|
+
data: writePath(existing ?? {}, layoutField, finalLayout),
|
|
105
|
+
draft: isDraftGlobal,
|
|
106
|
+
...locale ? {
|
|
107
|
+
locale: locale
|
|
108
|
+
} : {},
|
|
109
|
+
req,
|
|
110
|
+
overrideAccess: false,
|
|
111
|
+
user: req.user
|
|
112
|
+
});
|
|
113
|
+
const draftNote = isDraftGlobal ? DRAFT_NOTE : '';
|
|
114
|
+
return jsonResponse({
|
|
115
|
+
success: true,
|
|
116
|
+
message: `Patched ${layoutField} on global "${slug}". ` + `Operation: ${operation}. Block count: ${finalLayout.length}.` + draftNote,
|
|
117
|
+
blockCount: finalLayout.length,
|
|
118
|
+
operation
|
|
119
|
+
});
|
|
120
|
+
} catch (error) {
|
|
121
|
+
return errorResponse(`Error patching global "${slug}": ${errorMessage(error)}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
//# sourceMappingURL=patch-global-layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/tools/patch-global-layout.ts"],"sourcesContent":["import { z } from 'zod'\r\nimport type { PayloadRequest } from 'payload'\r\nimport type { BlockCatalog, BlockNestingMap } from '../types'\r\nimport { DRAFT_NOTE, errorMessage, jsonResponse, slugEnum, stampMcpContext } from './_helpers'\r\nimport {\r\n applyOperation,\r\n errorResponse,\r\n readPath,\r\n validateBlockList,\r\n writePath,\r\n} from './_layout-helpers'\r\n\r\n/**\r\n * patchGlobalLayout — surgical wrapper that mutates a single blocks-typed\r\n * field on a global without round-tripping the entire array through\r\n * updateGlobal. Same operation grammar as patchLayout (append, prepend,\r\n * insertAt, replaceAt, full); same recursive nesting validator against the\r\n * unified BlockNestingMap.\r\n *\r\n * Returns null when no global has any blocks field — the plugin entry\r\n * skips registration in that case, mirroring the schedulePublish pattern.\r\n */\r\nexport function createPatchGlobalLayoutTool(\r\n catalog: BlockCatalog,\r\n nesting: BlockNestingMap,\r\n draftGlobals: Set<string>,\r\n) {\r\n const allBlockSlugs = new Set(catalog.blocks.map((b) => b.slug))\r\n\r\n const nestingByGlobalField = new Map<string, string[]>()\r\n const nestingByBlockField = new Map<string, string[]>()\r\n for (const edge of nesting) {\r\n const key = `${edge.owner}:${edge.fieldPath}`\r\n if (edge.ownerType === 'global') {\r\n nestingByGlobalField.set(key, edge.acceptedBlockSlugs)\r\n } else if (edge.ownerType === 'block') {\r\n nestingByBlockField.set(key, edge.acceptedBlockSlugs)\r\n }\r\n }\r\n\r\n // Conditional registration: no global has a blocks field → no tool.\r\n if (nestingByGlobalField.size === 0) return null\r\n\r\n const patchableSlugs = [...new Set([...nestingByGlobalField.keys()].map((k) => k.split(':')[0]))]\r\n\r\n return {\r\n name: 'patchGlobalLayout',\r\n routing: { kind: 'global', action: 'update' } as const,\r\n description:\r\n 'Surgically modify a blocks-typed field on a global (e.g. footer sections, header nav) without sending the whole array. Same operation grammar as patchLayout. Use the blockNesting resource to see which slugs each field accepts; global-owned edges have ownerType \"global\".',\r\n parameters: {\r\n slug: slugEnum(patchableSlugs, 'global').describe(\r\n `The global slug whose blocks-typed field will be patched. One of: ${patchableSlugs.join(', ')}`,\r\n ),\r\n layoutField: z\r\n .string()\r\n .describe(\r\n 'Name (or dotted path) of the blocks-typed field on the global to patch — e.g. \"layout\", \"footer.sections\".',\r\n ),\r\n blocks: z\r\n .array(z.record(z.string(), z.unknown()))\r\n .describe(\r\n 'Blocks to compose. Each must have a `blockType` discriminator plus any block-specific fields. Nested blocks fields hold their own `blocks` arrays at any depth.',\r\n ),\r\n operation: z\r\n .enum(['append', 'prepend', 'insertAt', 'replaceAt', 'full'])\r\n .describe(\r\n 'How to apply the blocks: append (end), prepend (start), insertAt (at index), replaceAt (overwrite N starting at index), full (replace entire array — use with care).',\r\n ),\r\n insertIndex: z.number().optional().describe('Index for insertAt/replaceAt operations'),\r\n expectedUpdatedAt: z\r\n .string()\r\n .optional()\r\n .describe(\r\n 'Optional CAS guard. If set, the patch is rejected when the global\\'s current updatedAt differs from this value — protects against lost writes when concurrent edits race. Pass the updatedAt returned by a prior findGlobal call.',\r\n ),\r\n locale: z\r\n .string()\r\n .optional()\r\n .describe(\r\n 'Optional locale code (e.g. \"en\", \"fr\") to scope the patch to a single locale on localized blocks fields.',\r\n ),\r\n },\r\n handler: async (\r\n args: Record<string, unknown>,\r\n req: PayloadRequest,\r\n _extra: unknown,\r\n ) => {\r\n const { slug, layoutField, blocks, operation, insertIndex, expectedUpdatedAt, locale } =\r\n args as {\r\n slug: string\r\n layoutField: string\r\n blocks: Array<Record<string, unknown>>\r\n operation: 'append' | 'prepend' | 'insertAt' | 'replaceAt' | 'full'\r\n insertIndex?: number\r\n expectedUpdatedAt?: string\r\n locale?: string\r\n }\r\n\r\n const rootKey = `${slug}:${layoutField}`\r\n const rootAllowed = nestingByGlobalField.get(rootKey)\r\n if (!rootAllowed) {\r\n return errorResponse(\r\n `Field \"${layoutField}\" on global \"${slug}\" is not a blocks-typed field, or no nesting map entry exists for it.`,\r\n { availableFields: [...nestingByGlobalField.keys()] },\r\n )\r\n }\r\n\r\n const errors: string[] = []\r\n validateBlockList(blocks, rootAllowed, layoutField, allBlockSlugs, nestingByBlockField, errors)\r\n\r\n if (errors.length > 0) {\r\n return errorResponse('Block validation failed.', { errors })\r\n }\r\n\r\n stampMcpContext(req)\r\n\r\n let existing: Record<string, unknown> | undefined\r\n try {\r\n existing = (await req.payload.findGlobal({\r\n slug: slug as never,\r\n depth: 0,\r\n draft: true,\r\n ...(locale ? { locale: locale as never } : {}),\r\n req,\r\n overrideAccess: false,\r\n user: req.user,\r\n })) as Record<string, unknown> | undefined\r\n } catch (error) {\r\n return errorResponse(`Error fetching global \"${slug}\": ${errorMessage(error)}`)\r\n }\r\n\r\n if (expectedUpdatedAt !== undefined) {\r\n const currentUpdatedAt = (existing as { updatedAt?: unknown })?.updatedAt\r\n const currentStr =\r\n typeof currentUpdatedAt === 'string'\r\n ? currentUpdatedAt\r\n : currentUpdatedAt instanceof Date\r\n ? currentUpdatedAt.toISOString()\r\n : undefined\r\n if (currentStr !== expectedUpdatedAt) {\r\n return errorResponse(\r\n `Conflict: global \"${slug}\" was modified since expectedUpdatedAt (${expectedUpdatedAt}); current updatedAt is ${currentStr ?? 'unknown'}. Re-read the global and retry.`,\r\n { currentUpdatedAt: currentStr },\r\n )\r\n }\r\n }\r\n\r\n const currentLayout = readPath(existing, layoutField)\r\n const finalLayout = applyOperation(blocks, operation, insertIndex, currentLayout)\r\n\r\n const isDraftGlobal = draftGlobals.has(slug)\r\n\r\n try {\r\n await req.payload.updateGlobal({\r\n slug: slug as never,\r\n data: writePath(existing ?? {}, layoutField, finalLayout) as never,\r\n draft: isDraftGlobal,\r\n ...(locale ? { locale: locale as never } : {}),\r\n req,\r\n overrideAccess: false,\r\n user: req.user,\r\n })\r\n\r\n const draftNote = isDraftGlobal ? DRAFT_NOTE : ''\r\n return jsonResponse({\r\n success: true,\r\n message:\r\n `Patched ${layoutField} on global \"${slug}\". ` +\r\n `Operation: ${operation}. Block count: ${finalLayout.length}.` +\r\n draftNote,\r\n blockCount: finalLayout.length,\r\n operation,\r\n })\r\n } catch (error) {\r\n return errorResponse(`Error patching global \"${slug}\": ${errorMessage(error)}`)\r\n }\r\n },\r\n }\r\n}\r\n\r\n"],"names":["z","DRAFT_NOTE","errorMessage","jsonResponse","slugEnum","stampMcpContext","applyOperation","errorResponse","readPath","validateBlockList","writePath","createPatchGlobalLayoutTool","catalog","nesting","draftGlobals","allBlockSlugs","Set","blocks","map","b","slug","nestingByGlobalField","Map","nestingByBlockField","edge","key","owner","fieldPath","ownerType","set","acceptedBlockSlugs","size","patchableSlugs","keys","k","split","name","routing","kind","action","description","parameters","describe","join","layoutField","string","array","record","unknown","operation","enum","insertIndex","number","optional","expectedUpdatedAt","locale","handler","args","req","_extra","rootKey","rootAllowed","get","availableFields","errors","length","existing","payload","findGlobal","depth","draft","overrideAccess","user","error","undefined","currentUpdatedAt","updatedAt","currentStr","Date","toISOString","currentLayout","finalLayout","isDraftGlobal","has","updateGlobal","data","draftNote","success","message","blockCount"],"mappings":"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"}
|
|
@@ -17,6 +17,10 @@ import type { BlockCatalog, BlockNestingMap } from '../types';
|
|
|
17
17
|
*/
|
|
18
18
|
export declare function createPatchLayoutTool(catalog: BlockCatalog, nesting: BlockNestingMap, draftCollections: Set<string>): {
|
|
19
19
|
name: string;
|
|
20
|
+
routing: {
|
|
21
|
+
readonly kind: "collection";
|
|
22
|
+
readonly action: "update";
|
|
23
|
+
};
|
|
20
24
|
description: string;
|
|
21
25
|
parameters: {
|
|
22
26
|
collection: z.ZodString;
|
|
@@ -26,12 +30,5 @@ export declare function createPatchLayoutTool(catalog: BlockCatalog, nesting: Bl
|
|
|
26
30
|
operation: z.ZodEnum<["append", "prepend", "insertAt", "replaceAt", "full"]>;
|
|
27
31
|
insertIndex: z.ZodOptional<z.ZodNumber>;
|
|
28
32
|
};
|
|
29
|
-
handler: (args:
|
|
30
|
-
collection: string;
|
|
31
|
-
documentId: string;
|
|
32
|
-
layoutField?: string;
|
|
33
|
-
blocks: Array<Record<string, unknown>>;
|
|
34
|
-
operation: "append" | "prepend" | "insertAt" | "replaceAt" | "full";
|
|
35
|
-
insertIndex?: number;
|
|
36
|
-
}, req: PayloadRequest, _extra: unknown) => Promise<import("./_helpers").McpTextResponse>;
|
|
33
|
+
handler: (args: Record<string, unknown>, req: PayloadRequest, _extra: unknown) => Promise<import("./_helpers").McpTextResponse>;
|
|
37
34
|
};
|